数据类型的转换、格式化、校验
1、数据绑定流程
1. Spring MVC 主框架将 ServletRequest 对象及目标方
法的入参实例传递给 WebDataBinderFactory 实例,以创
建 DataBinder 实例对象
2. DataBinder 调用装配在 Spring MVC 上下文中的
ConversionService 组件进行数据类型转换、数据格式
化工作。将 Servlet 中的请求信息填充到入参对象中
3. 调用 Validator 组件对已经绑定了请求消息的入参对象
进行数据合法性校验,并最终生成数据绑定结果
BindingData 对象
4. Spring MVC 抽取 BindingResult 中的入参对象和校验
错误对象,将它们赋给处理方法的响应入参
自定义类型转换器(了解)
前台页面
<form action="testConversionServiceConverer" method="post">
Employee:<input type="text" name="employee" />
<input type="submit" value="Submit" />
</form>
转换器类(要实现converter接口)
package com.oracle.converters;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import com.oracle.entities.Department;
import com.oracle.entities.Employee;
@Component
public class MyConverter implements Converter<String, Employee> {
@Override
public Employee convert(String source) {
if(source != null){
String[] vals = source.split("-");
if(vals != null&&vals.length==3){
String lastName = vals[0];
Integer gender = Integer.parseInt(vals[1]);
Department department = new Department();
department.setId(Integer.parseInt(vals[2]));
Employee employee = new Employee(null, lastName, gender, department);
return employee;
}
}
return null;
}
}
spring mvc配置文件中配置
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- 配置自定义拦截器 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myConverter"/>
</set>
</property>
</bean>
在控制类中使用
@RequestMapping("testConversionServiceConverer")
public String test(@RequestParam("employee") Employee employee){
employeeDao.save(employee);
return "redirect:/springmvc/listall";
}
关于 mvc:annotation-driven
<mvc:annotation-driven /> 会自动注
册RequestMappingHandlerMapping
、RequestMappingHandlerAdapter 与
ExceptionHandlerExceptionResolver 三个bean。
还将提供以下支持:
支持使用 ConversionService 实例对表单参数进行类型转换 –
支持使用 @NumberFormat annotation、@DateTimeFormat –
注解完成数据类型的格式化
支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证 –
支持使用 @RequestBody 和 @ResponseBody 注解
InitBinder注解
注解的作用:
由 @InitBinder 标识的方法,可以对 WebDataBinder 对
象进行初始化
WebDataBinder的作用
①WebDataBinder 是 DataBinder 的子类,用
于完成由表单字段到 JavaBean 属性的绑定
②完成数据类型的转换,数据校验
③如果在数据转换和校验的过程中,出现错误信息,通过返回BindingResult,显示错误信息
实例:
@InitBinder
public void initBinder(WebDataBinder binder){
binder.setDisallowedFields("lastName");
}
数据格式化
操作步骤
①在spring mvc 的配置文件中配置<mvc:annotation-driven></mvc:annotation-driven>
②在对应的实体类的属性上加上注解,例如
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth;
@NumberFormat(pattern="#,###,#.#")
private float salary;
格式化原理
对属性对象的输入/输出进行格式化,从其本质上讲依然
属于 “类型转换” 的范畴。
Spring 在格式化模块中定义了一个实现
ConversionService 接口的
FormattingConversionService 实现类,该实现类扩展
了 GenericConversionService,因此它既具有类型转换的
功能,又具有格式化的功能
FormattingConversionService 拥有一个
FormattingConversionServiceFactroyBean 工厂类,
后者用于在 Spring 上下文中构造前者
FormattingConversionServiceFactroyBean 内部已经注册了 :
NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性 –
使用 @NumberFormat 注解
JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期– 类型
的属性使用 @DateTimeFormat 注解
装配了 FormattingConversionServiceFactroyBean 后,就可
以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动
了。<mvc:annotation-driven/> 默认创建的
ConversionService 实例即为
FormattingConversionServiceFactroyBean
BindingResult
BindingResult中存放了处理的异常信息,可以通过入参是获取到BindingResult,进而获取到异常信息
@RequestMapping(value="/add",method=RequestMethod.POST)
public String save(Employee employee,BindingResult result){
System.out.println(employee);
if(result.getErrorCount()>0){
for(FieldError error:result.getFieldErrors()){
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
}
employeeDao.save(employee);
return "redirect:/springmvc/listall";
}
数据校验
①. 使用 JSR 303 验证标准
JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,
它已经包含在 JavaEE 6.0 中
②. 加入 hibernate validator 验证框架的 jar 包
在官网下载hibernate validator的jar包
加入以下六个jar包
classmate-1.1.0.jar
hibernate-validator-5.2.1.Final.jar
hibernate-validator-annotation-processor-5.2.1.Final.jar
hibernate-validator-cdi-5.2.1.Final.jar
jboss-logging-3.2.1.Final.jar
validation-api-1.1.0.Final.jar
③. 在 SpringMVC 配置文件中添加 <mvc:annotation-driven />
④. 需要在 bean 的属性上添加对应的注解
@NotEmpty
private String lastName;
@Email
private String email;
⑤. 在目标方法 bean 类型的前面添加 @Valid 注解
通过在处理方法的入参上标
注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行
数据校验的工作
2). 验证出错转向到哪一个页面 ?
注意: 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
错误消息的显示和国际化
jsp页面显示
显示全部错误消息
<form:errors path="*"></form:errors>
显示在对应的记录位置
LastName:<form:input path="lastName"/><br>
<form:errors path="lastName"></form:errors>
国际化错误消息
当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看
WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认
的错误消息,否则使用国际化消息。
①i18n.properties
当一个属性校验失败后,校验框架会为该属性生成 4 个消
息代码,这些代码以校验注解类名为前缀,结合
modleAttribute、属性名及属性类型名生成多个对应的消
息代码
例如
NotEmpty.employee.lastName=u540Du5B57u4E0Du80FDu4E3Au7A7A
Past.employee.birth = u751Fu65E5u5FC5u987Bu662Fu4E00u4E2Au8FC7u53BBu7684u65F6u95F4
②配置国际化消息
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
③补充
若数据类型转换或数据格式转换时发生错误,或该有的参
数不存在,或调用处理方法时发生错误,都会在隐含模型
中创建错误消息。其错误代码前缀说明如下:
required:必要的参数不存在。如 @RequiredParam(“param1”) –
标注了一个入参,但是该参数不存在
typeMismatch:在数据绑定时,发生数据类型不匹配的问题 –
methodInvocation:Spring MVC 在调用处理方法时发生了错误
ajax
①加入jackson的jar包
jackson-annotations-2.4.0.jar
jackson-core-2.4.5.jar
jackson-databind-2.4.6.1.jar
②前台发送ajax请求
$(function(){
$("#id").click(function(){
var url = this.href;
var args={};
$.post(url,args,function(data){
for(var i=0;i<data.length;i++){
var id = data[i].id
var lastName = data[i].lastName
alert(id+":"+lastName);
}
});
return false;
});
})
③后台通过配置@ResponseBody和方法的返回值类型返回json数据
@ResponseBody
@RequestMapping("ajax")
public Collection<Employee> testJson(){
return employeeDao.getEmployees();
}
实现原理
通过HttpMessageConverter<T>
上传下载
①添加jar包
commons-fileupload-1.2.1.jar
commons-io-2.0.jar
②配置springmvc 的配置文件
<!-- 配置 MultipartResolver -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="1024000"></property>
</bean>
上传的页面
<form action="testFileUpload" method="POST" enctype="multipart/form-data">
File: <input type="file" name="file"/>
Desc: <input type="text" name="desc"/>
<input type="submit" value="Submit"/>
</form>
后台处理
@RequestMapping("/testFileUpload")
public String testFileUpload(@RequestParam("desc") String desc,
@RequestParam("file") MultipartFile file) throws IOException{
System.out.println("desc: " + desc);
System.out.println("OriginalFilename: " + file.getOriginalFilename());
System.out.println("InputStream: " + file.getInputStream());
return "success";
}
下载的页面
<a href="testResponseEntity">Test ResponseEntity</a>
后台处理
@RequestMapping("/testResponseEntity")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException{
byte [] body = null;
ServletContext servletContext = session.getServletContext();
InputStream in = servletContext.getResourceAsStream("/files/abc.txt");
body = new byte[in.available()];
in.read(body);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename=abc.txt");
HttpStatus statusCode = HttpStatus.OK;
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(body, headers, statusCode);
return response;
}
国际化
关于国际化:
1. 在页面上能够根据浏览器语言设置的情况对文本(不是内容), 时间, 数值进行本地化处理
2. 可以在 bean 中获取国际化资源文件 Locale 对应的消息
3. 可以通过超链接切换 Locale, 而不再依赖于浏览器的语言设置情况
解决:
1. 使用 JSTL 的 fmt 标签
2. 在 bean 中注入 ResourceBundleMessageSource 的示例, 使用其对应的 getMessage 方法即可
3. 配置 LocalResolver 和 LocaleChangeInterceptor
拦截器
①实现HandlerInterceptor接口
package com.atguigu.springmvc.interceptors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class FirstInterceptor implements HandlerInterceptor{
/**
* 该方法在目标方法之前被调用.
* 若返回值为 true, 则继续调用后续的拦截器和目标方法.
* 若返回值为 false, 则不会再调用后续的拦截器和目标方法.
*
* 可以考虑做权限. 日志, 事务等.
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("[FirstInterceptor] preHandle");
return true;
}
/**
* 调用目标方法之后, 但渲染视图之前.
* 可以对请求域中的属性或视图做出修改.
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[FirstInterceptor] postHandle");
}
/**
* 渲染视图之后被调用. 释放资源
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("[FirstInterceptor] afterCompletion");
}
}
②配置文件中配置
<mvc:interceptors>
<!-- 配置自定义的拦截器 -->
<bean class="com.atguigu.springmvc.interceptors.FirstInterceptor"></bean>
</mvc:interceptors>
拦截器补充
<mvc:interceptors>
<!-- 配置自定义的拦截器 -->
<!-- <mvc:mapping path="/emps"/>配置拦截器作用的路径,<mvc:exclude-mapping path="/emps"/>配置拦截器不作用的路径 -->
<mvc:interceptor>
<mvc:mapping path="/emps"/>
<bean class="com.atguigu.springmvc.interceptors.SecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
多个拦截器的执行顺序
preHandle正序
postHandle和afterCompletion倒序
异常处理
Spring MVC 通过 HandlerExceptionResolver 处理程序
的异常,包括 Handler 映射、数据绑定以及目标方法执行
时发生的异常。
ExceptionHandlerExceptionResolver
主要处理 Handler 中用 @ExceptionHandler 注解定义的
方法。用ModelAndView返回异常信息
/**
* 1. 在 @ExceptionHandler 方法的入参中可以加入 Exception 类型的参数, 该参数即对应发生的异常对象
* 2. @ExceptionHandler 方法的入参中不能传入 Map. 若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值
* 3. @ExceptionHandler 方法标记的异常有优先级的问题. 越接近发生的异常的优先级越高
* 4. @ControllerAdvice: 如果在当前 Handler 中找不到 @ExceptionHandler 方法来出来当前方法出现的异常,
* 则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常.
*/
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("----> 出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常.
package com.atguigu.springmvc.test;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class SpringMVCTestExceptionHandler {
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("----> 出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
}
ResponseStatusExceptionResolver
自定义一个返回的页面
可以作用在类上,也可以是方法上
①注册
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
}
②使用
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
if(i == 13){
throw new UserNameNotMatchPasswordException();
}
System.out.println("testResponseStatusExceptionResolver...");
return "success";
}
也可以直接放在方法上,当调用此方法时,返回异常的页面
@ResponseStatus(reason="测试",value=HttpStatus.NOT_FOUND)
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
if(i == 13){
throw new UserNameNotMatchPasswordException();
}
System.out.println("testResponseStatusExceptionResolver...");
return "success";
}
DefaultHandlerExceptionResolver
对一些特殊的异常进行处理,比
如NoSuchRequestHandlingMethodException、HttpReques
tMethodNotSupportedException、HttpMediaTypeNotSuppo
rtedException、HttpMediaTypeNotAcceptableException
等。
SimpleMappingExceptionResolver
如果希望对所有异常进行统一处理,可以使用
SimpleMappingExceptionResolver,它将异常类名映射为
视图名,即发生异常时使用对应的视图报告异常
<!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionAttribute" value="ex"></property>
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>