1.HiddenHttpMethodFilter、HttpPutFormContentFilter
HiddenHttpMethodFilter
可以把 POST 请求转为 DELETE 或 POST 请求 可以把 POST 请求转为 DELETE 或 PUT请求
①web.xml配置
<!-- 配置 org.springframework.web.filter.HiddenHttpMethodFilter: 可以把 POST 请求转为 DELETE 或 POST 请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
② 页面处使用方式
<form action="springmvc/testRest/1" method="post">
<!-- 设置请求方式 -->
<input type="hidden" name="_method" value="PUT/DELETE">
<input type="submit" value="TestRest PUT/DELETE"/>
</form>
HttpPutFormContentFilter
在Spring3.0中获取put表单的参数,即使用HttpPutFormContentFilter过滤器
①web.xml配置
<filter>
<filter-name>httpPutFormcontentFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpPutFormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注: 该过滤器只能接受enctype值为application/x-www-form-urlencoded
的表单,也就是说,在使用该过滤器时,form表单的代码必须如下:
<form action="" method="put" enctype="application/x-www-form-urlencoded">
...
</form>
2.使用 Servlet API 作为入参
MVC 的 Handler 方法可以接受哪些 ServletAPI 类型的参数
- HttpServletRequest
- HttpServletResponse
- HttpSession
- java.security.Principal
- Locale
- InputStream
- OutputStream - Reader
- Writer
3.@RequestParam, @RequestBody
@RequestParam
A) 常用来处理简单类型的绑定,通过Request.getParameter() 获取的String可直接转换为简单类型的情况( String–> 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值;
B)用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容,提交方式GET、POST;
C) 该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定;
示例代码:
@Controller
@RequestMapping("/pets")
@SessionAttributes("pet")
public class EditPetForm {
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
application/x-www-form-urlencoded方式是Jquery的Ajax请求默认方式,这种方式的好处就是浏览器都支持,在请求发送过程中会对数据进行序列化处理,以键值对形式?key1=value1&key2=value2的方式发送到服务器,如果用Jquery,它内部已经进行了处理,如果自己写原生的Ajax请求,就需要自己对数据进行序列化。
application/json,随着json规范的越来越流行,并且浏览器支持程度原来越好,许多开发人员易application/json作为请求content-type,告诉服务器请求的主题内容是json格式的字符串,服务器端会对json字符串进行解析,这种方式的好处就是前端人员不需要关心数据结构的复杂度,只要是标准的json格式就能提交成功,application/json数据格式越来越得到开发人员的青睐。
@RequestBody
该注解常用来处理Content-Type: 不是application/x-www-form-urlencoded
编码的内容,例如application/json, pplication/xml等;
它是通过使用HandlerAdapter 配置的HttpMessageConverters来解析post data body,然后绑定到相应的bean上的。
因为配置有FormHttpMessageConverter
,所以也可以用来处理 application/x-www-form-urlencoded
的内容,处理完的结果放在一个MultiValueMap<String, String>
里,这种情况在某些特殊需求下使用,详情查看ormHttpMessageConverter api;
示例代码:
@RequestMapping(value = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
4.@SessionAttributes、@SessionAttribute
@SessionAttributes
若希望在多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个 @SessionAttributes, Spring MVC
将在模型中对应的属性暂存到 HttpSession 中。
注意: 该注解只能放在类的上面. 而不能修饰放方法.
@SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中
@SessionAttributes(types=User.class) 会将隐含模型中所有类型为 User.class 的属性添加到会话中。
@SessionAttributes(value={“user1”, “user2”})
@SessionAttributes(types={User.class, Dept.class})
@SessionAttributes(value={“user1”, “user2”},types={Dept.class})
如果在处理类定义处标注了@SessionAttributes(“xxx”),则尝试从会话中获取该属性,并将其赋给该入参,然后再用请求消息填充该入参对象。如果在会话中找不到对应的属性,则抛出 HttpSessionRequiredException 异常
避免@SessionAttributes引发的异常
//该方法会往隐含模型中添加一个名为xxx的模型属性
@ModelAttribute("XXX")
public XXX getXXX(){
XXX xxx = new XXX();
return xxx;
}
@SessionAttribute
如果你需要访问预先存在的、以全局方式管理的会话属性的话,比如在控制器之外(比如通过过滤器)可能或不可能存在在一个方法参数上使用注解@SessionAttribute:
/**
* 在处理请求 /helloWorld/jump 的时候,会在会话中添加一个 sessionStr 属性。
* <p/>
* 这里可以通过@SessionAttribute 获取到
*/
@RequestMapping("/sesAttr")
public String handleSessionAttr(@SessionAttribute(value = "sessionStr") String sessionStr, Model model)
{
System.out.println("--> sessionStr : " + sessionStr);
model.addAttribute("sth", sessionStr);
return "/examples/targets/test1";
}
5.@ModelAttribute
有 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用!
@ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
2). SpringMVC 会一 value 为 key, POJO 类型的对象为 value, 存入到 request 中.
@ModelAttribute(value = "user")
public User getUser(@RequestParam("id") Integer id){
if(id!=null) {
System.out.println("id = " + id);
User user = new User(1, "tom", "123", "tom@qq.com", "SHENZHEN");
System.out.println("模拟从数据库根据id获取的user:" + user);
return user;
}
return null;
}
@RequestMapping("/testModelAttribute")
public void update(User user){
System.out.println("更新user:" + user);
}
6.自定义视图解析器
springmvc配置文件
<!-- 配置视图 BeanNameViewResolver 解析器: 使用视图的名字来解析视图 -->
<!-- 通过 order 属性来定义视图解析器的优先级, order 值越小优先级越高 -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100"></property>
</bean>
自定义视图解析器
@Component
public class HelloView implements View{
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
response.getWriter().print("hello view, time: " + new Date());
}
}
controller渲染时 return “XXX”;返回自定义视图 解析器的bean的name
7.静态资源
<mvc:default-servlet-handler/>
若将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理,因找不到对应处理器将导致错误。
可以在 SpringMVC 的配置文件中配置
<mvc:default-servlet-handler/>
的方式解决静态资源的问题:<mvc:default-servlet-handler/>
将在 SpringMVC 上下文中定义一个DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由DispatcherServlet 继续处理。一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的WEB 服务器的默认 Servlet 名称不是 default,则需要通过 defaultservlet-name 属性显式指定。
<mvc:resources />
<!--将链接中/p的静态的访问路径映射为URL,常用于加载html、js、css、图片、视频等静态资源-->
<mvc:resources mapping="/js/**" location="classpath:/pageSource/js/"/>
<mvc:resources mapping="/img/**" location="classpath:/pageSource/img/"/>
<mvc:resources mapping="/css/**" location="classpath:/pageSource/css/"/>
8.数据绑定流程
- Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
- DataBinder 调用装配在 Spring MVC 上下文中的ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
- 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData 对象
- Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
@InitBinder
- 由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用
于完成由表单字段到 JavaBean 属性的绑定 - @InitBinder方法不能有返回值,它必须声明为void。
- @InitBinder方法的参数通常是是 WebDataBinder
数据格式化
- FormattingConversionServiceFactroyBean 内部已经注册了 :
NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性 使用 @NumberFormat 注解
JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性 使用 @DateTimeFormat 注解 - 装配了 FormattingConversionServiceFactroyBean 后,就可以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动
了。<mvc:annotation-driven/>
默认创建的ConversionService 实例即为FormattingConversionServiceFactroyBean
@DateTimeFormat
@DateTimeFormat 注解可对java.util.Date、java.util.Calendar、java.long.Long 时间类型进行标注:
– pattern 属性:类型为字符串。指定解析/格式化字段数据的模式,如:”yyyy-MM-dd hh:mm:ss”
– iso 属性:类型为 DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式,包括四种:ISO.NONE(不使用) – 默
认、ISO.DATE(yyyy-MM-dd) 、ISO.TIME(hh:mm:ss.SSSZ)、ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)
– style 属性:字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式:S:短日期/时间格式、M:中日期/时间格式、L:长日期/时间格式、F:完整日期/时间格式、-:忽略日期或时间格式
@NumberFormat
@NumberFormat 可对类似数字类型的属性进行标注,它拥有两个互斥的属性:
– style:类型为 NumberFormat.Style。用于指定样式类型,包括三种:Style.NUMBER(正常数字类型)、
Style.CURRENCY(货币类型)、 Style.PERCENT(百分数类型)
– pattern:类型为 String,自定义样式,如patter=”#,###”;
@DateTimeFormat(pattern="yyyy/MM/dd")
private Date birthday;
@NumberFormat(pattern="#,###.##")
数据校验
JSR 303中含有的注解
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
------------------------------
Hibernate Validator 附加的注解
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
Spring 本身并没有提供JSR 303 的实现,所有必须将JSR 303的实现者的jar 包(HIbernate Validator jar包)导入到项目中
<!--JSR 303的实现者的jar 包(HIbernate Validator jar包)-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.4.Final</version>
</dependency>
在表单/命令对象类的属性中标注校验注解,处理方法对应的入参前添加 @Valid,
这个保存校验结果的入参必须是 BindingResult 或Errors
注:需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
提示消息的国际化
• 每个属性在数据绑定和数据校验发生错误时,都会生成一 个对应的 FieldError 对象。
• 当一个属性校验失败后,校验框架会为该属性生成 4 个消 息代码,这些代码以校验注解类名为前缀,结合
modleAttribute、属性名及属性类型名生成多个对应的消 息代码:例如 User 类中的 password 属性标准了一个
@Pattern 注 解,当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4 个错误代码:
– Pattern.user.password
– Pattern.password
– Pattern.java.lang.String
– Pattern
• 当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看 WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认 的错误消息,否则使用国际化消息。
• 若数据类型转换或数据格式转换时发生错误,或该有的参 数不存在,或调用处理方法时发生错误,都会在隐含模型
中创建错误消息。其错误代码前缀说明如下:
– required:必要的参数不存在。如 @RequiredParam(“param1”)标注了一个入参,但是该参数不存在
– typeMismatch:在数据绑定时,发生数据类型不匹配的问题
– methodInvocation:Spring MVC 在调用处理方法时发生了错误
超链接切换 Locale
<!-- 配置国际化资源文件 -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
<!--可以通过超链接切换 Locale, 而不再依赖于浏览器的语言设置情况
配置 LocalResolver 和 LocaleChangeInterceptor-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" >
</bean>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" ></bean>
</mvc:interceptors>
9.关于 mvc:annotation-driven
<mvc:annotation-driven />
会自动注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter 与ExceptionHandlerExceptionResolver 三个bean。- 还将提供以下支持:
支持使用 ConversionService 实例对表单参数进行类型转换
支持使用 @NumberFormat annotation、@DateTimeFormat注解完成数据类型的格式化
支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
支持使用 @RequestBody 和 @ResponseBody 注解
10.异常处理
ExceptionHandlerExceptionResolver
- 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
- @ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,但是声明的异常有
RuntimeException 和 Exception,此候会根据异常的最近继承关系找到继承深度最浅的那个 @ExceptionHandler
注解方法,即标记了 RuntimeException 的方法 - ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找@ControllerAdvice 中的@ExceptionHandler 注解方法
ResponseStatusExceptionResolver
• 在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。
• 定义一个 @ResponseStatus 注解修饰的异常类:
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public class UnauthorizedException extend RuntimeException{}
• 若在处理器方法中抛出了上述异常:
若ExceptionHandlerExceptionResolver 不解析述异常。由于触发的异常 UnauthorizedException 带有@ResponseStatus
注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus.UNAUTHORIZED 代码给客户端。HttpStatus.UNAUTHORIZED 代表响应码401,无权限。关于其他的响应码请参考 HttpStatus 枚举类型源码。
DefaultHandlerExceptionResolver
• 对一些特殊的异常进行处理,比如
NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、
HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等,
请参考 DefaultHandlerExceptionResolver.doResolveException()
源码。
SimpleMappingExceptionResolver
• 如果希望对所有异常进行统一处理,可以使用SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常.
<!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 页面显示异常信息 ${exceptionAttribute.value} 默认为${exception} -->
<!--<property name="exceptionAttribute" value="ex"></property>-->
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
11.SpringMVC工作流程
Spring工作流程
用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
HandlerExecutionChain mappedHandler = null; mappedHandler = getHandler(processedRequest);
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
执行拦截器 applyPreHandle 方法
`if (!mappedHandler.applyPreHandle(processedRequest, response)) {return; }`
提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller) ,并返回一个ModelAndView对象。
// Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
- HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
- 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
- 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
- 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
- 执行拦截器 applyPostHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
- 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet , ViewResolver 结合Model和View,来渲染视图
- 执行拦截器triggerAfterCompletion方法
- 执行拦截器 applyPostHandle方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
render(mv, request, response);
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
最后将渲染结果返回给客户端。