2. Spring MVC 高级技术
2.1 拦截器(Inteceptor)使用
1. 监听器、过滤器和拦截器对比
- Servlet: 处理Request请求和Response响应 
- 过滤器(Filter): 对Request请求起到过滤的作用,作用在Servlet之前,如果配置为/*可以对所 有的资源访问(servlet、js/css静态资源等)进行过滤处理 
- 监听器(Listener): 实现了javax.servlet.ServletContextListener 接口的服务器端组件,它随 Web应用的启动而启动,只初始化一次,然后会一直运行监视,随Web应用的停止而销毁 
作用一: 做一些初始化工作,web应用中spring容器启动ContextLoaderListener
作用二: 监听web中的特定事件,比如HttpSession,ServletRequest的创建和销毁;变量的创建、 销毁和修改等。可以在某些动作前后增加处理,实现监控,                  比如统计在线人数,利用 HttpSessionLisener等。
- 拦截器(Interceptor): 是SpringMVC、Struts等表现层框架自己的,不会拦截 jsp/html/css/image的访问等,只会拦截访问的控制器方法(Handler)。
从配置的⻆度也能够总结发现:serlvet、filter、listener是配置在web.xml中的,而interceptor是 配置在表现层框架自己的配置文件中的

2. 拦截器的执行流程
在运行程序时,拦截器的执行是有一定顺序的,该顺序与配置文件中所定义的拦截器的顺序相关。 单个
拦截器,在程序中的执行流程如下图所示:

- 程序先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方 法,否则将不再向下执行。 
- 在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过 DispatcherServlet向客户端返回响应。 
- 在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。 
3. 多个拦截器的执行流程
多个拦截器(假设有两个拦截器Interceptor1和Interceptor2,并且在配置文件中, Interceptor1拦截 器配置在前),在程序中的执行流程如下图所示:

从图可以看出,当有多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置 顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行。
示例代码
自定义SpringMVC拦截器
| 
 
 public class MyIntercepter01 implements HandlerInterceptor {
 
 
 
 
 
 
 
 
 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 System.out.println("MyIntercepter01 preHandle......");
 return true;
 }
 
 
 
 
 
 
 
 
 @Override
 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
 System.out.println("MyIntercepter01 postHandle......");
 }
 
 
 
 
 
 
 
 
 @Override
 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
 System.out.println("MyIntercepter01 afterCompletion......");
 }
 }
 
 | 
注册SpringMVC拦截器
| <mvc:interceptors>
 
 
 <mvc:interceptor>
 
 <mvc:mapping path="/**"/>
 
 
 <bean class="com.lagou.edu.interceptor.MyIntercepter01"/>
 </mvc:interceptor>
 <mvc:interceptor>
 <mvc:mapping path="/**"/>
 <bean class="com.lagou.edu.interceptor.MyIntercepter02"/>
 </mvc:interceptor>
 </mvc:interceptors>
 
 | 
2.2 处理multipart形式的数据
文件上传
原生servlet处理上传的文件数据的,springmvc又是对serlvet的封装
添加所需jar包
|  <dependency>
 <groupId>commons-fileupload</groupId>
 <artifactId>commons-fileupload</artifactId>
 <version>1.3.1</version>
 </dependency>
 
 | 
配置文件上传解析器
| <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
 
 <property name="maxUploadSize" value="1000000000"/> </bean>
 
 | 
前端Form
|  <%--
 1 method="post"
 2 enctype="multipart/form-data" 3 type="file"
 --%>
 <form method="post" enctype="multipart/form-data" action="/demo/upload">
 <input type="file" name="uploadFile"/>
 <input type="submit" value="上传"/> </form>
 
 | 
后台接收Handler
| @RequestMapping("upload")public String upload(MultipartFile uploadFile, HttpServletRequest request)
 throws IOException {
 
 String originalFilename = uploadFile.getOriginalFilename();
 
 String extendName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1, originalFilename.length());
 String uuid = UUID.randomUUID().toString();
 
 String newName = uuid + "." + extendName;
 String realPath = request.getSession().getServletContext().getRealPath("/uploads");
 
 String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
 File floder = new File(realPath + "/" + datePath);
 if(!floder.exists()) {
 floder.mkdirs();
 }
 uploadFile.transferTo(new File(floder,newName));
 return "success";
 }
 
 | 
2.3 在控制器中处理异常
| @ControllerAdvice
 public class GlobalExceptionResolver {
 
 @ExceptionHandler(ArithmeticException.class)
 public ModelAndView handleException(ArithmeticException exception, HttpServletResponse response) {
 ModelAndView modelAndView = new ModelAndView();
 modelAndView.addObject("msg", exception.getMessage());
 modelAndView.setViewName("error");
 return modelAndView;
 }
 }
 
 | 
2.4 基于Flash属性的跨重定向请求数据传递
重定向时请求参数会丢失,我们往往需要重新携带请求参数,我们可以进行手动参数拼接如下:
return "redirect:handle01?name=" + name;
但是上述拼接参数的方法属于get请求,携带参数⻓度有限制,参数安全性也不高,此时,我们可以使 用SpringMVC提供的flash属性机制,向上下文中添加flash属性,框架会在session中记录该属性值,当 跳转到⻚面之后框架会自动删除flash属性,不需要我们手动删除,通过这种方式进行重定向参数传递, 参数⻓度和安全性都得到了保障,如下:
| 
 
 
 
 
 
 @RequestMapping("/handleRedirect")
 public String handleRedirect(String name,RedirectAttributes redirectAttributes) {
 
 
 
 redirectAttributes.addFlashAttribute("name",name);
 return "redirect:handle01";
 }
 
 |