SpringMVC框架整理
Posted Nothing_doit
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC框架整理相关的知识,希望对你有一定的参考价值。
SpringMVC框架整理第二发,数据绑定流程,数据校验(错误信息国际化),拦截器,异常处理。
数据绑定流程(数据转换,数据格式化,数据校验)
1. Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
2. DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
3. 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象
4. Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是 DataBinder,运行机制如下:
使用binderFactory创建一个绑定器,WebDataBinder WebDataBinder binder = binderFactory.createBinder(request, attribute, name); if (binder.getTarget() != null) //绑定数据 bindRequestParameters(binder, request); //校验数据 validateIfApplicable(binder, parameter); //错误信息 if (binder.getBindingResult().hasErrors()) if (isBindExceptionRequired(binder, parameter)) throw new BindException(binder.getBindingResult());
数据绑定流程中的组件介绍:
①数据类型转换组件(了解它可以自定义类型转换器)
ConversionService 是 Spring 类型转换体系的核心接口。 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个 ConversionService. Spring 将自动识别出 IOC 容器中的 ConversionService,并在 Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换 可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器
②验证组件(在服务器端对用户信息的合法性进行验证)
③错误消息保存组件(用于保存数据校验后的错误信息,然后在页面就可以进行获取)
组件详细介绍:
ConversionService
conversionService在工作的时候,里面装配了很多的xxxConvertor,用于数据在不同类型之间的转换。 其中的一个数据类型转换器 final class StringToCharacterConverter implements Converter<String, Character>@Override public Character convert(String source) if (source.length() == 0) return null; if (source.length() > 1) throw new IllegalArgumentException( "Can only convert a [String] with length of 1 to a [Character]; string value '" + source + "' has length of " + source.length()); return source.charAt(0);
自定义类型转换器
数据绑定核心思想: 1、WebDataBinder用来将请求过来的数据和指定的对象进行绑定 2、WebDataBinder里面注册了 1)有负责数据转换/格式化工作的ConversionService组件。 这个组件在工作的时候使用他里面各种转换器和格式化器进行工作 2)还注解了一堆验证组件Validators 3)还将数据绑定的结果(失败信息)放在BindingResult
在熟知了数据绑定的流程我们可以自定义类型转换器并让其工作:
需求: 使用表单提交value="1-tom-email-男-dd",提交后框架可以帮助我们自动包装成对象。框架默认并没有符合我们需求的类型转换器,我们需要自定义一个
表单 <form action="$pageContext.request.contextPath /save" method="post"> <input type="text" name="emp" value="1-tom-email-男-dd"> <input type="submit" value="提交"> </form>
①编写一个自定义类型转换器实现Converter<S,T> 需要实现public interface Converter<S, T>接口 自定义类型转换器 package com.atguigu.convert; import org.springframework.core.convert.converter.Converter; import com.atguigu.entities.Employee;
/** * 创建自定义的类型转换器,将我们的字符串转换成对象 * @author LENOVO * */ public class EmployeeConvert implements Converter<String, Employee>
@Override public Employee convert(String src) System.out.println(src); if(src!=null&&!"".equals(src)) //将其利用split根据分割线拆分成数组 String[] split = src.split("-"); Employee employee = new Employee(Integer.parseInt(split[0]), split[1], split[2], 0, null); return employee; ; return null; ②在ConversionService组件的创建工厂中注册自定义转换器 <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <!--在ConversionService组件中创建工厂中注册自定义的转换器--> <bean class="com.atguigu.convert.EmployeeConvert"></bean> </set> </property> </bean> ③告诉SpringMVC用这个工厂创建ConversionService 组件 <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
数据格式化: 在SpringMVC框架中格式化器和类型转换器都在ConversionService 组件中,我们在数据类型转换的时候,还可以根据指定的格式进行格式化并转换 但是我们自定义的类型转换器,不具有格式化功能。
原因: ConversionServiceFactoryBean造出的ConversionService组件是DefaultConversionService,不具有格式化功能 而默认配置的ConversionService组件是DefaultFormattingConversionService
配置既可以实现类型转换(含自定义类型转换)和数据格式功能 <!--配置一个既有格式化功能又能数据类型转换的创建工厂 --> <bean id="formattingConversionServiceFactoryBean" class="org.springframework.format.support. FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.atguigu.convert.EmployeeConvert"></bean> </set> </property> </bean> <!--告诉SpringMVC用这个工厂创建ConversionService组件--> <mvc:annotation-driven conversion-service="formattingConversionServiceFactoryBean"/>
提供给测试所有的handle /** * 自定义类型转化器的测试 * @param employee * @return */ @RequestMapping(value="/save",method=RequestMethod.POST) public String save(@RequestParam("emp") Employee employee) System.out.println(employee); return "input";
annotation-driven细节
既没有配置 <mvc:default-servlet-handler/> 也没有配置 <mvc:annotation-driven/>
使用默认的适配器帮我们执行方法 现象:可以处理正常的请求,但是不能访问静态资源
配置 <mvc:default-servlet-handler/>没有配置 <mvc:annotation-driven/>
我们发现AnnotationMethodHandlerAdapter没有,所以不能正常干活了。所以请求无法处理 现象:静态资源能访问,动态请求又不行了
配置<mvc:default-servlet-handler/>也配置<mvc:annotation-driven/>
现象:静态资源可以访问,动态请求也可以处理
<!--若配置直接映射,会把初始干活的干掉, 但是配置上<mvc:annotation-driven/>,高级干活的RequestMappingHandlerAdapter不会被干掉 --> <mvc:view-controller path="/abc" view-name="input"/> 配置直接映射出现的情况:没有帮我们处理请求的适配器了。
综上: < mvc:annotation-driven />(单独配置它,无法访问静态资源) < mvc:default-servlet-handler />(把这个配上就有可以访问静态资源了) 因此当我们使用SpringMVC框架做表现层开发时这两个配置是标配
数据校验
JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中 . JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。常用的校验注解:
Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解 扩展注解
Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。 在 Spring MVC 中,可直接通过注解驱动的方式进行数据校验 Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。 <mvc:annotation-driven/> 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作
Spring 本身并没有提供 JSR303 的实现,所以必须将 JSR303 的实现者的 jar 包放到类路径下。
数据校验的方式:
在已经标注了 JSR303 注解的表单/命令对象前 标注一个 @Valid,Spring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验 Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或 Errors 类型,这两个类都位于 org.springframework.validation 包中 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参 Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或 getFieldErrors(String field) BindingResult 扩展了 Errors 接口如何获取校验结果:
在目标方法中获取校验结果 在表单/命令对象类的属性中标注校验注解,在处理方法对应的入参前添加 @Valid,Spring MVC 就会实施校验并将校验结果保存在被校验入参对象之后的 BindingResult 或 Errors 入参中。 常用方法: FieldError getFieldError(String field) List<FieldError> getFieldErrors() Object getFieldValue(String field) Int getErrorCount()如何在页面上显示错误:
Spring MVC 除了会将表单/命令对象的校验结果保存到对应的 BindingResult 或 Errors 对象中外, 还会将所有校验结果保存到"隐含模型” 即使处理方法的签名中没有对应于表单/命令对象的结果入参,校验结果也会保存在 “隐含对象” 中。隐含模型中的所有数据最终将通过 HttpServletRequest 的属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取错误信息 在 JSP 页面上可通过 <form:errors path=“userName”> 显示错误消息
实现服务器端数据校验测试:
①导入hibernate validator相关jar包 classmate-0.8.0.jar hibernate-validator-5.0.0.CR2.jar hibernate-validator-annotation-processor-5.0.0.CR2.jar validation-api-1.1.0.CR1.jar jboss-logging-3.1.1.GA.jar
若是因为tomacat服务器版本太低,发生El异常,导入增强版的ELjar包 把这三个增强版的ELjar包放到tomacat的lib中
②给javabean添加验证规则(准备校验的对象) public class Employee
private Integer id; @NotEmpty private String lastName; @Email private String email; //1 male, 0 female private Integer gender;
private Department department; //指定日期提交的格式 @Past @DateTimeFormat(pattern="yyyy-MM-dd") private Date birth;
③给要验证的参数加@Valid,添加的验证规则才有效 @RequestMapping(value="/save",method=RequestMethod.POST) public String save( @Valid Employee employee, BindingResult result ) System.out.println(employee); //打印错误信息 System.out.println(result.getAllErrors()); //返回输入页面 return "input";
④给tomcat添加增强版el(若是出现el异常,导增强版的eljar包) 这时的错误提示消息只是框架根据本地,显示的默认错误信息,我们想要让他显示我们自己的错误消息 ⑤错误消息国际化,配置国际化模块,指定国际化文件的基本名(并进行配置这样显示的就是咱们自己设置的国际化提示信息) 在配置文件中配置 <!--配置国际化模块 只要国际化文件中有错误消息的代码对应的值,取出错误消息就是按照国际化文件制定的值 注意:id一定要是messageSource --> <bean id="messageSource" class="org.springframework.context.support. ResourceBundleMessageSource "> <!-- 指定国际化文件的基本名 --> <property name="basename" value="i18n"></property> </bean> 编写国际化文件(这两个文件要放在src目录下) 国际化文件中消息的key, 格式 : 验证规则-bean名称-属性名=错误提示
i18n_en_US.properties NotEmpty.employee.lastName=lastName must not Empty NotEmpty.employee.email=email must not Empty NotEmpty.employee.birth=birth must not Empty Past.employee.birth=birth Must Be a PastTime typeMismatch.employee.birth=typeMismatch
i18n_zh_CN.properties NotEmpty.employee.lastName=\\u59D3\\u540D\\u4E0D\\u80FD\\u4E3A\\u7A7A NotEmpty.employee.email=\\u90AE\\u7BB1\\u4E0D\\u80FD\\u4E3A\\u7A7A Email.employee.email=\\u90AE\\u7BB1\\u683C\\u5F0F\\u4E0D\\u6B63\\u786E Past.employee.birth=\\u751F\\u65E5\\u5FC5\\u987B\\u662F\\u4E00\\u4E2A\\u8FC7\\u53BB\\u7684\\u65F6\\u95F4 typeMismatch.employee.birth=\\u522B\\u4E71\\u5199
表单: <form:form action="$pageContext.request.contextPath /save" modelAttribute="employee" method="POST"> 员工姓名:<form:input path="lastName"/> <!--获取设置验证规则的属性错误信息-->
<form:errors path="lastName"></form:errors><br/> 员工邮箱:<form:input path="email"/> <!-- 当邮箱格式错误时显示邮箱错误(使用这个表单在页面获取错误信息) --> <form:errors path="email"></form:errors><br/> 员工性别: 女:<form:radiobutton path="gender" value="0"/> 男:<form:radiobutton path="gender" value="1"/><br/> 所在部门: <form:select path="department.id" items="$depts " itemLabel="departmentName" itemValue="id"></form:select> <!-- 测试格式化问题,日期之间用/表示不会报错,用-会报错 可以在实体类处使用@DateFormt指定日期格式 --> <br/>员工生日:<form:input path="birth"/> <!-- 直接使用这个属性就可以将错误信息显示在页面 --> <form:errors path="birth"></form:errors> <br/><br/> <input type="submit" value="保存"/> </form:form> 若要上边的表单可以动态获取部门信息,需要配置一个@ModelAttribute注解修饰的方法,提前获取部门信息。 @ModelAttribute public Employee employeeModel(Map<String, Object> map) // 1、查出所有的部门,去页面要显示成下拉列表 Collection<Department> collection = departmentDao.getDepartments();/**/ // 2、将查出的部门信息放在隐含模型中。自动放在request域中 map.put("depts", collection); return new Employee();
处理请求的handle: @RequestMapping(value="/save",method=RequestMethod.POST) public String save(@Valid Employee employee, BindingResult result ) System.out.println(employee); System.out.println(result.getAllErrors()); return "input";
处理json
①导包并准备一个SpringMVC框架运行环境(web.xml配置,配置文件) jackson-annotations-2.1.5.jar jackson-core-2.1.5.jar jackson-databind-2.1.5.jar 处理json并不需要对配置文件做其他额外配置 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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><context:component-scan base-package="com.atguigu"></context:component-scan>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- 标配 --> <mvc:annotation-driven/> <mvc:default-servlet-handler/> </beans> ②编写一个index.jsp接收json <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> <script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script> <script type="text/javascript"> $(function() $("a:first").click(function() $.get("$pageContext.request.contextPath /getjson",function(data) alert(data[0].lastName); ,"json"); return false; ); ); </script> </head> <body> <!-- json实现,直接使用@ResponseBody,然后将需要转为json的数据返回就行,导入json的三个jar包 --> <a href="$pageContext.request.contextPath /getjson">获取json</a> </body> </html>
③写一个handle 这里直接利用上边的实体类 @Autowired private EmployeeDao employeeDao;
@ResponseBody @RequestMapping("/getjson") public Collection<Employee> getJson() Collection<Employee> all = employeeDao.getAll(); return all;
这样就好了,就可以在页面拿到json了
文件下载
①导包 commons-fileupload-1.2.1.jar commons-io-2.0.jar②同样,配置好框架运行环境编写一个jsp页面 <a href="download">下载</a>
③处理文件下载的handle /** * 使用SpringMVC实现下载 * @return */ @RequestMapping("/download") public ResponseEntity<byte[]> download() //下载的东西D:\\一会看的东西.txt HttpHeaders headers = new HttpHeaders(); //添加响应头 headers.add("Content-Disposition", "attachment;filename=abc.txt"); byte[] byteArray = null; try byteArray = IOUtils.toByteArray(new FileInputStream("D:/一会看的东西.txt")); catch (Exception e) e.printStackTrace(); //body响应体,headers响应头,status响应状态码 ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(byteArray, headers, HttpStatus.OK); return responseEntity; 然后就可以使用下载了
文件上传
①导包 commons-fileupload-1.2.1.jar commons-io-2.0.jar ②配置文件上传的解析器 <!-- 配置一个文件上传的解析器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 可以指定一些配置信息 --> <property name="defaultEncoding" value="utf-8"></property> <property name="maxUploadSize" value="#1024*1024*20"></property> </bean> ③处理文件上传请求 @RequestMapping("/upload") public String upload(MultipartFile file) System.out.println("获取到请求"); //上传到D:/upload try file.transferTo(new File("D:/upload/" + file.getOriginalFilename())); catch (IllegalStateException | IOException e) // TODO Auto-generated catch block e.printStackTrace(); return "index";④扩展-多文件上传 /** * 多文件上传要指定file * @param file * @return */ @RequestMapping("/upload") public String upload(@RequestParam("file") MultipartFile[] file) System.out.println("获取到请求"); //上传到D:/upload try for (MultipartFile multipartFile : file)
multipartFile.transferTo(new File("D:/upload/" + multipartFile.getOriginalFilename())); catch (IllegalStateException | IOException e) // TODO Auto-generated catch block e.printStackTrace(); return "index";
补充: @InitBinder 由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定 @InitBinder方法不能有返回值,它必须声明为void。 @InitBinder方法的参数通常是是 WebDataBinder
例如:设置一下不允许绑定的字段 /** * 在绑定器执行执行可以给绑定器指定一些规则,例如设置某些属性不需要绑定 */ @InitBinder public void initBinder(WebDataBinder binder) //设置不允许绑定的字段 binder.setDisallowedFields("lastName");
拦截器
拦截器的作用: 在目标方法执行前后,进行一些预处理工作、进行一些扫尾工作 SpringMVC中拦截器的接口: HandlerInterceptor 该接口主要方法: preHandle //在目标方法执行之前会被执行 boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;postHandle //在目标方法执行之后会执行 void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception; afterCompletion //请求响应完成后执行 void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
原理:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try ModelAndView mv = null; Exception dispatchException = null;
try processedRequest = checkMultipart(request); multipartRequestParsed = processedRequest != request;
//Determine handler for the current request. //动态资源使用RequestMappingHandlerMapping处理映射 //静态资源 RequestMappingHandlerMapping是没有用的, //SimpleUrlHandlerMapping来处理静态资源和处理器的映射 //所有的静态资源默认都是使用DefaultServletHttpRequestHandler来处理 mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) noHandlerFound(processedRequest, response); return;
// Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method);
以上是关于SpringMVC框架整理的主要内容,如果未能解决你的问题,请参考以下文章
myBatis+Spring+SpringMVC框架面试题整理