解决问题:
1.数据类型转换 (页面提交的所有数据都是字符串 。如String age=request.getParameter("age"));
2.数据格式(如 提交的日期进行转换 birth=2017-12-15-----> Date 格式是2017/12/15 还是 2017.12.15还是。。。)
3.数据校验(我们提交的数据必须是合法的?前端:js+正则表达式。后端:也是必要的。 )
数据绑定流程原理(debug看源码可以结合尚硅谷雷丰阳老师的视频,这里不分析源码)
Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。
数据绑定的核心部件是 DataBinder(数据绑定器),运行机制如下:
Spring MVC 抽取 BindingResult (绑定期间,效验的结果保存)中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
自定义类型转换器
ConversionService 是 Spring 类型转换体系的核心接口。
Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactoryBean 中:
Converter<S,T>:将 S 类型对象转为 T 类型对象(就用这个,不管其他2个)
一.数据类型转换
步骤:
1)ConversionService;是一个接口;
里面有converter(转换器)进行工作;
(动手操作粗体)
实现converter接口,写一个自定义的类型转化器
Converter是ConversionService中的组件
1 你的converter得放进ConversionService中
2 将webDataBinder中的conversionservice设置成我们这个加了自定义类型转换器的conversionservice
3 配置出ConversionService,让springmvc用我们的ConversionService
4 源码上webDataBinder上的conversionservice组件就替换了,虽然替换了,但是默认的类型转换(比如string-->int之类的)还在,只是增加了刚刚定义的那一个。
例子1,在上次crud的基础上增加一个快速添加的功能。
思路:
1,编辑页面,点击快速添加。
2,去到控制层,获取参数,希望转数据类型(string-->employee)。
3,自定义一个数据类型转换器类(继承Converter,把 string 转为 employee)。
4,去springmvc配置文件里注册一个自定义数据类型转换类。
<!--list.jsp里加上--> <form:form action="${ctp}/quickAdd"> <!--将员工的所有信息都写上,自动封装对象--> <input name="empinfo" value="empAdmin-admin@qq.com-1-101"> <input type="submit" value="快速添加"> </form:form>
package com.atguigu.control; import com.atguigu.bean.Employee; import com.atguigu.dao.DepartmentDao; import com.atguigu.dao.EmployeeDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; /** * Created by Zhuxiang on 2020/6/5. */ @Controller public class EmployeeController { @Autowired EmployeeDao employeeDao; @Autowired DepartmentDao departmentDao; /** * list传过来是 quickAdd?empinfo=empAdmin-admin@qq.com-1-101 * @RequestParam("empinfo") Employee employee 意思是 * ===> Employee employee=request.getParameter("empinfo")(转不了!) * 必须要自己写一个自定义类型的转换器。 * 去到component包,编写mystringToEmployeeConverter */ @RequestMapping("/quickAdd") public String quickAdd(@RequestParam("empinfo") Employee employee){ System.out.println("最后结果是 "+employee); employeeDao.save(employee); return "redirect:/emps"; } }
自定义一个数据类型转换器
package com.atguigu.component; import com.atguigu.bean.Department; import com.atguigu.bean.Employee; import com.atguigu.dao.DepartmentDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; /** * Created by Zhuxiang on 2020/6/5. * Converter<S,T> * s:source * T:target */ public class MyStringToEmployeeConverter implements Converter<String, Employee> { @Autowired DepartmentDao departmentDao; @Override public Employee convert(String s) { System.out.println("页面提交的要转的字符串"+s); if(s!=null){ String[] strs = s.split("-"); String lastName = strs[0]; String email = strs[1]; Integer gender = Integer.parseInt(strs[2]); Integer deptId = Integer.parseInt(strs[3]); Department dept = departmentDao.getDepartment(deptId); Employee employee = new Employee(null,lastName,email,gender,dept); System.out.println(s+"--converter--"+employee); return employee; }else{ return null; } } }
在springmvc配置文件里注册。
<!--使用自己配置的conversionservice类型转换组件,而不是默认的--> <mvc:annotation-driven conversion-service="myConversionService"/> <!--告诉springmvc别用默认的conversionservice,用我们自定义的实现了convert接口的类--> <!-- <bean id="myConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">--> <!--以后写自定义类型转换器的时候,就使用 FormattingConversionServiceFactoryBean 来注册 既具有类型转换又具有格式化功能(如日期格式化)--> <bean id="myConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <!--converters转换器中添加我们自定义的类型转换器--> <property name="converters"> <set> <bean class="com.atguigu.component.MyStringToEmployeeConverter"></bean> </set> </property> </bean>
二,数据格式
- 关于 <mvc:annotation-driven /> 作用
l <mvc:annotation-driven /> 会自动注册:
RequestMappingHandlerMapping 、RequestMappingHandlerAdapter 与
ExceptionHandlerExceptionResolver 三个bean。
l 还将提供以下支持:
n 支持使用 ConversionService 实例对表单参数进行类型转换
n 支持使用 @NumberFormat、@DateTimeFormat 注解完成数据类型的格式化
n 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
n 支持使用 @RequestBody 和 @ResponseBody 注解
1) 日期格式化概述
1) 实验代码(格式化日期)
格式化:页面提交的数据格式如果不正确,就报400
日期格式2017-12-19
Conversionservicefactorybean:创建的conversionservice组件是没有格式化器存在的。
所以如果要用自定义的conversionservice
(类型转换器)。
@RequestMapping("/haha")
public ResponseEntity<String> haha(){
springMVC提供了拦截器机制;允许运行目标方法之前进行一些拦截工作,或者目标方法运行之后进行一些其他处理;
filter;是javaweb提供的拦截器。
拦截器(HandlerInterceptor):是springMVC提供的拦截器。
preHandle(httpservletrequest,httpservletresponse):在目标方法运行之前调用,返回boolean; return· true,(chain.doFilter())放行;return false,不放行。
postHandle(httpservletrequest,httpservletresponse,modelandview):在目标方法运行之后调用 ;目标方法调用之后。
afterCompletion(httpservletrequest,httpservletresponse,exception):请求整个完成之后,来到目标页面之后;chain.doFilter()放行;资源响应之后。
1)拦截器是一个接口
2)实现HandlerInterceptor接口。
3)配置拦截器
4)拦截器的正常运行流程
拦截器的preHandle方法--->目标方法--->拦截器postHandle--->页面--->拦截器的afterCompletion
MyFirstInterceptor...preHandle...
test01...
MyFirstInterceptor...postHandle...
success.jsp...
MyFirstInterceptor...afterCompletion...
其他流程:
1,只要preHandle不放行就没有以后的流程了。
2,只要放行了,afterCompletion就都会执行。
多个拦截器
正常流程:
MySecondInterceptor...preHandle...
MyFirstInterceptor...preHandle...
test01...
MyFirstInterceptor...postHandle...
MySecondInterceptor...postHandle...
success.jsp...
MyFirstInterceptor...afterCompletion...
MySecondInterceptor...afterCompletion...
异常流程:
1.不放行;
1)如果MyFirstInterceptor...preHandle...不放行,那么它前面已经放行了的拦截器的afterCompletion还是会执行。
MySecondInterceptor...preHandle...
MyFirstInterceptor...preHandle...
MySecondInterceptor...afterCompletion...
流程:filter的流程;
拦截器的 preHandle 按顺序执行
拦截器的 postHandle 按逆序执行
拦截器的 afterCompletion 按逆序执行
前面已经放行了的拦截器的afterCompletion还是会执行
问题:什么时候用filter,什么时候用拦截器?
filter是javaweb3大组件之一,由tomcat执行,随着tomcat的启动而启动,
而拦截器是在ioc容器里的,由springMVC执行。
如果某些功能需要其他组件配合完成,我们就使用拦截器。
其他简单的情况,就用filter,它就一个dofilter方法。
springmvc配置国际化就不写了,可以去看尚硅谷springmvc的视频。
异常处理 springmvc里自带3个异常解析器。
解析是按顺序来的,1 2 3,如果都不能处理就抛出给tomcat。
1 ExceptionHandlerExceptionResolver
主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
2 ResponseStatusExceptionResolver
在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。
3 DefaultHandlerExceptionResolver
判断是否springmvc自带的异常
springmvc自己的异常,如HttpRequestMethodNotSupportedException,1,2异常解析器都没管的话。
springmvc运行流程。
1,所有请求,前端控制器(DispatcherServlet)收到请求,调用doDispatch进行处理
DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI):
判断请求URI对应的映射
1)不存在:
a)再判断是否配置了mvc:default-servlet-handler:
b)如果没配置,则控制台报映射查找不到,客户端展示404错误
c)如果有配置,则执行目标资源(一般为静态资源,如:JSP,HTML)
2)存在:继续
2,根据HandlerMapping中保存的请求映射信息找到,处理当前请求的,处理器执行链(包含拦截器)
3,根据当前处理器找到他的HandlerAdapter(适配器)
4,拦截器的preHandle先执行
5,适配器执行目标方法,并返回ModelAndView。
1),ModelAttribute注解标注的方法提前运行
2),执行目标方法的时候(确定目标方法用的参数)
1),有注解
2),没注解:
1)看是否Model,map以及其他的
2)如果是自定义类型
1)从隐含模型中看有没有,如果有就从隐含模型中拿
2)如果没有,再看是否SessionAttribute标注的属性,如果是从session中拿,如果拿不到会抛异常。
3)都不是,就利用反射创建对象。
6,拦截器的postHandle执行
7,处理结果;(页面渲染流程)
1),如果有异常使用异常解析器处理异常;处理完后还会返回ModelAndView
2),调用render进行页面渲染
1),视图解析器根据视图名得到视图对象
2),视图对象调用render方法;
3),执行拦截器的afterCompletion;
springMVC和spring整合
springMVC和spring整合的目的,分工明确。
springMVC的配置文件就来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,支持ajax。。。);
spring的配置文件来配置和业务有关的(事务控制,数据源。。。);
问题:bean被创建两次。
方式一(不推荐),springMVC配置文件里加上<import resource="spring.xml">
再分别进行springmvc和spring的文件配置,虽然分开写配置文件了,但是还是在同一个ioc容器里。
方式二(推荐),使用exclude-filter 和 include-filter 子节点来规定只能扫描的注解。