关于SpringMVC的使用总结
- 简介
springMVC即Spring Web MVC,是spring web模块的一部分,是spring自己的web框架
springMVC对Servlet API 进行了完善的封装,极大的简化了开发人员的编程工作。同时springMVC也提供了友好简便的方式让开发人员可以使用Servlet API,十分灵活。
- Servlet的操作方式
- springMVC的操作方式
maven 依赖和插件配置:
<dependencies> <!-- spring最基本的环境支持依赖,会传递依赖core、beans、expression、aop等基本组件,以及commons-logging、aopalliance --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!-- 提供了对其他第三方库的内置支持,如quartz等 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!-- spring处理对象关系映射的组件,传递依赖了jdbc、tx等数据库操作有关的组件 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!-- spring对面向切面编程的支持,传递依赖了aspectjweaver --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!-- spring处理前端表现层的组件,即springMVC,传递依赖了web操作有关的组件 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!-- 数据校验,springMVC需要用到 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.4.Final</version> </dependency> <!-- json解析,springMVC需要用到 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.6.0</version> </dependency> <!-- 文件上传,springMVC需要用到 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> <!-- servlet依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- jsp依赖 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <!-- jstl依赖 --> <dependency> <groupId>org.glassfish.web</groupId> <artifactId>jstl-impl</artifactId> <version>1.2</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> <exclusion> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <!-- 指定JDK编译版本 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
- 核心控制器——DispatcherServlet
springMVC提供了一个核心控制器(大总管)——DispatcherServlet,负责统一调用springMVC提供的其他组件,按照固定步骤处理请求,生成响应。
在web.xml中配置DispatcherServlet :
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
- DispatcherServlet结构
- springMVC的核心配置文件
springMVC是spring的一部分,需要spring上下文的支持,所以需要一个像beans.xml一样的配置文件,我们把这个配置文件命名为dispatcher-servlet.xml(其他名称也可以)
除了以bean的方式配置、管理springMVC的组件外,springMVC的其他内容也需要在这个文件中配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 配置扫描spring注解时扫描的包,同时也开启了spring注解支持 --> <context:component-scan base-package="com.rupeng.web" /> <!-- 开启springMVC相关注解支持 --> <mvc:annotation-driven /> <!-- 视图解析器,视图页面的全路径为 prefix + viewName + suffix --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- 文件上传解析器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--整个请求的最大大小,用来限制上传的文件大小--> <property name="maxUploadSize" value="20971520" /> <property name="defaultEncoding" value="UTF-8" /> <!--延迟解析,以便捕获文件大小超出限制的异常,方便生成错误提示信息--> <property name="resolveLazily" value="true"/> </bean> <!-- 资源映射 --> <mvc:resources location="/css/" mapping="/css/**" /> <mvc:resources location="/js/" mapping="/js/**" /> <mvc:resources location="/images/" mapping="/images/**" /> </beans>
这个文件中配置的都是和web有关的内容,至于项目的其他部分,比如service、dao等也可以配置在这个文件里面。但更一般的,会初始化两个spring上下文,一个用来管理springMVC有关的内容,另一个用来管理项目其他内容
- 额外的字符编码过滤器
springMVC还提供了一个字符编码过滤器,可以对POST请求和响应设置编码(tomcat8会自动对GET请求进行编码):
<!-- 设置post请求编码和响应编码 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <!-- 为true时也对响应进行编码 --> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- DispatcherServlet处理时序图
URL—handler映射
springMVC通过封装,替开发人员做了很多事情,对于那些必须由开发人员来实现的部分,也提供了一整套机制让开发人员实现起来更加简单
开发人员通过实现自己的handler,对请求进行具体的业务处理。实现方式:
-
声明一个普通类并使用@Controller(不能使用其他方式)把该类标注成spring的bean
-
使用@RequestMapping把该类的方法标注为handler
@RequestMapping有两个常用的属性:value和method
value指定匹配的请求路径,单独使用时可省略此属性名
method 指定匹配的请求方法(GET、POST),值为枚举类型RequestMethod
@RequestMapping也可以标注在类上,为此类所有的handler统一指定"父路径"
- handler方法的参数类型
handler方法的参数支持很多类型,不同的类型会有不同的用法和效果
支持Servlet API
即HttpServletRequest、HttpServletResponse、HttpSession等。
如果handler方法声明了以上类型的参数,方法执行时springMVC会把和当前请求相关的这些对象注入到方法参数。
其实只要拿到了request、response对象,完全可以按照普通的Servlet方式处理当前请求,这也体现了springMVC高度的灵活性。
还可以通过RequestContextHolder工具类在任何springMVC环境下访问Servlet API:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
支持常用类型
包括基本类型及其包装类型、String、Date等
如果handler方法有以上类型的参数,再配合使用相关的注解,springMVC可以自动从请求中获取相应的值,并且进行类型转换等。
常用注解:
- @RequestParam 获取指定请求参数,相当于request.getParameter()
- @RequestHeader 获取指定请求头,相当于request.getHeader()
- @CookieValue 获得指定cookie的value值
- @SessionAttribute 获取指定session域的属性值,相当于session.getAttribute()
注意:这几个注解的required属性默认为true,即值不能为null
特别的,新版本的springMVC(比如当前使用的版本),@RequestParam可以省略,springMVC会根据参数名称尝试操作,且允许值为null
对于Date类型的参数,由于springMVC在简体中文环境下默认的日期格式不太适用,可自定义日期格式:
@InitBinder protected void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true)); }
支持pojo类
如果handler方法的参数是pojo类型,可以配合使用@ModelAttribute,springMVC会自动创建此pojo类对象,并尝试从请求参数中获取pojo字段相应的值、类型转换、数据绑定等
特别的,可以省略@ModelAttribute
特别的,可以配合使用@Valid、BindingResult,对pojo类的字段进行有效性校验
支持Map、Model、ModelMap
这三个实际上是一样的。即使不在handler方法中声明Model参数,springMVC内部也会对每个请求创建一个Model对象,而且持有pojo参数(如果有)的引用。
Model中可以存入一些键值对数据,最终Model中的数据会被添加到request中,所以也可以直接把键值对存入request。这么看来Model似乎是多余的,其实Model可以和spring自定义标签配合使用,为其提供pojo对象
此外,handler方法还支持其他类型,如RedirectAttributes、MultipartFile等
- handler方法的返回值类型
支持String、ModelAndView
String类型的返回值表示视图名称
ModelAndView就相当于方法参数Model + 返回值String,比较常用
特别的,视图名称使用redirect: 前缀表示重定向到另一个handler(不使用视图解析器),配合使用RedirectAttrbutes方法参数,springMVC会生成带参数的重定向URL
特别的,视图名称使用forward: 前缀表示转发到另一个handler(不使用视图解析器)
支持pojo类
对于ajax请求来说,一般需要返回json格式的字符串数据。springMVC提供了@ResponseBody注解,当标注在handler方法的返回值之前时,可以自动把返回对象转换为json格式的字符串并发送到客户端。
springMVC把对象转换为json格式字符串使用的是Jackson工具包,maven依赖如下(已配置好):
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.6.0</version> </dependency>
对于Date类型的字段,可以使用Jackson提供的@JsonFormat指定日期格式(标注在字段上)
支持void
如果handler方法的返回值类型是void,则springMVC会认为开发人员已经使用Servlet API 生成了响应,就不再继续处理了。
特别的,无论返回值类型是什么,return null和void含义是一样的。
- 视图层
可以使用JSP页面作为视图层,常用的技术组合为JSP + EL + JSTL(+ spring form标签库)
spring form标签库
spring提供了两个标签库,主要的是form标签库,常用的有<form>、<input>、<checkboxes>、<radiobuttons>、<select>、<errors>等
在使用这些标签时,要求handler方法需要有pojo参数,并且其他标签需要放到<form>标签内部
- <form>会生成HTML的form标签,主要用来为其他spring标签提供Model中的pojo对象。modelAttribute属性值需要和pojo参数名一致
- <input> 默认生成HTML的input(text)标签,path属性值为pojo类的某个字段名
- <checkboxes> 使用数组、集合生成一组input(checkbox)标签
<form:checkboxes items="${allRoleList }" path="roleList" itemLabel="name" itemValue="id" />
<radiobuttons> 和<checkboxes>用法相似
- <select> 和<checkboxes>用法相似,可以使用multiple="false"属性指定不允许多选
- <errors>可以输出某个字段的验证失败信息,也可以输出全部字段的验证失败信息
<form>的action属性不支持JSP表达式<%= %>,可以使用${pageContext.request.contextPath}的方式取得项目路径
这里对于日期字段来说,可以使用@DateTimeFormat标注在字段上指定日期格式
在实际使用时,既可以使用springMVC提供的这套标签,也可以使用EL+JSTL的方式。如果对页面样式要求较高,可能并不适合使用这些spring form标签
- 数据校验
当handler方法的参数是pojo类时,就可以使用spring提供的一套数据校验方案对pojo类中的字段值进行有效性校验
使用hibernate提供的校验实现
maven依赖(已配置好):
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.4.Final</version> </dependency>
把校验器配置为bean,id需要指定为validator:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/> </bean>
校验相关注解
@Valid 标注在handler方法的pojo参数前面,相当于此pojo对象的校验开关
pojo参数后面需要紧跟BindingResult参数,记录校验结果。
可以使用spring form标签库的<errors>标签把校验失败信息输出在页面上
下面的注解都标注在字段上,表示对标注的字段应用某种校验规则,可以直接在注解上指定校验失败时的错误信息(不指定时使用默认的)
@NotEmpty 此字段不能为null,也不能为空字符串
@Length 可以指定字符串的最小、最大长度
@Min 指定数值的最小值
@Max 指定数值的最大值
@Range 指定数值的范围
@Size 指定数组、集合的元素范围
@AssertTrue 断定boolean值为true
@AssertFalse
@Email 指定字符串符合email格式
@Pattern 字符串符合指定的正则表达式
在实际使用时,既可以采用这种校验方式,也可以使用普通的校验方式,即手动对需要校验的字段进行校验,然后向request中添加校验失败的信息,然后在页面中使用EL表达式把失败信息输出到页面上,普通校验方式大家比较熟悉,而且非常灵活。
- springMVC拦截器
springMVC的interceptor和Servlet的filter非常相似,可以拦截请求进行预处理和后处理
- 实现HandlerInterceptor接口编写自己的拦截器(或者继承HandlerInterceptorAdapter)
- 在dispatcher-servlet.xml文件中配置此拦截器
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/xx/*" /> <mvc:exclude-mapping path="/xx/xx.do"/> <bean class="xx" /> </mvc:interceptor> </mvc:interceptors>
可以使用若干个<mvc:mapping>指定拦截的请求,支持 * 通配符
也可以使用<mvc:exclude-mapping>指定不拦截的请求,一般和<mvc:mapping>配合使用从拦截的请求中排除某些请求
servlet的filter调用链和springMVC的interceptor调用链执行时候的差别:
- 文件上传
springMVC封装了Commons FileUpload 普通文件上传方式,简化了文件上传操作。(此外还支持servlet3.0方式的文件上传)
普通文件上传方式
maven依赖(已配置好):
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
文件上传解析器,id需要指定为multipartResolver(已配置好):
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--整个请求的最大大小,用来限制上传的文件大小--> <property name="maxUploadSize" value="20971520" /> <property name="defaultEncoding" value="UTF-8" /> <!--延迟解析,以便捕获文件大小超出限制的异常,方便生成错误提示信息--> <property name="resolveLazily" value="true"/> </bean>
接下来在handler方法上使用MultipartFile类型的参数即可(@RequestParam 注解可省略)
MultipartFile 常用方法:
getContentType() 获得上传文件的类型
getOriginalFilename() 获得文件原始名称
getSize() 获得上传文件的大小
getInputStream() 获得上传文件的读取流
transferTo(dest) 把上传的文件复制到目标文件
当然,还对页面的form表单有要求:<form method="post" enctype="multipart/form-data">
处理文件大小超出限制时的错误提示信息:
@ExceptionHandler(MultipartException.class) public ModelAndView handleException(MultipartException ex) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", "上传文件的大小不能超过20M"); return modelAndView; }
- 异常解析器
通过实现HandlerExceptionResolver接口可以编写自己的异常解析器,注册为bean后可以捕获服务器运行时抛出的异常
通常会在异常解析器中记录异常日志信息,还可以根据请求的类型(普通请求、ajax请求)生成合适的错误提示
@Component public class MyHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //实际项目中要改为记录日志 ex.printStackTrace(); //判断请求的类型 if (request.getHeader("X-Requested-With") != null) { //ajax请求 try { response.getWriter().print("{\"status\":\"error\", \"data\":\"服务器出错了\"}"); } catch (IOException e) { //实际项目中要改为记录日志 e.printStackTrace(); } //此处返回没有视图名称的ModelAndView对象表示开发人员自己生成了响应 return new ModelAndView(); } else { //普通请求 return new ModelAndView("500"); } } }