拦截器的概述:
拦截器是SpringMVC中的一个核心应用组件,主要用于处理多个
Controller的共性问题.当我们的请求由DispatcherServlet派发
到具体Controller之前首先要执行拦截器中一些相关方法,在这些
方法中可以对请求进行相应预处理(例如权限检测,参数验证),这些方法可以决定对这个请求进行拦截还是放行.
>>服务器一启动,就会创建拦截器对象,
>>对拦截器配置延迟加载是没有用的
>>拦截器是单例的,整个过程,拦截器只有一个实例对象
拦截器需要实现 HandleInterceptor接口,或者继承HandlerInterceptorAdaptor抽象类;
HandlerInterceptor接口的三个方法:
1,preHandle()
2,postHandle()
3,afterCompletion()
拦截器的执行过程:
详解:当页面的request请求发送过来,由前端控制器DispatcherServlet来控制,交给对应的Controller进行请求的处理,但是在处理之前,要先经过拦截器的拦截(如果配置了的话),在由对应controller处理之前,先执行对应的
1,preHandle()是拦截器最先执行的方法,是在请求到达Controller之前执行的,其实就是拦截器用于拦截请求的,
三个参数,分别是request,response,handelr就是这个请求要去找的后端处理器Controller.方法的返回值是bloolean类型,如果返回为false,就说明请求在此终结,不能执行后面的代码了.如果返回值为true,那么这个拦截器就要放行,将请求交给后端处理器Controller.
2. postHandler
这个方法,是在后端控制器controller处理完请求之后,就执行的,这个方法,多了一个参数,ModelAndView
后端控制器controller处理请求可能需要返回页面和数据,所以会多一个ModelAndView,但是这个方法,是在渲染页面之前执行的,渲染热面是交个前端控制器来完成的.
3.afterCompletion:拦截器最后执行的方法,
拦截器在spring-configs.xml的配置:
或者使用注解的方式,
在拦截器上添加@Service注解,
那么就不用使用bean,使用ref引用就行;
>>>>拦截器链:
当我们系统中有多个拦截器时,这些拦截器可以构成一个拦截器链.其原理类似过滤器中的过滤链。在多个拦截器应用中仅当所有匹配的拦截器的preHandle()都执行之后,才会调用Controller中处理请求的方法,然后再执行所有匹配的拦截器的postHandler(),再执行所有匹配的拦截器的afterCompletion()。
在拦截器链中,各拦截器的执行先后顺序取决于配置文件中配置的节点的先后顺序!
拦截器的执行顺序:(假设都能放行)
拦截器链的配置:
线程安全问题:
场景:假设需要做一个记录方法执行的时间的拦截器,
在cntroller处理请求之前,要进入这个事件拦截器的preHandler方法,这个时候,会记录一下当前的时间作为startTime,当请求处理完毕,视图渲染结束之后,再取得一次系统时间作为方法运行结束的时间,由于跨了方法,所以strtTime必须定义成全局变量,否则afterCompletion方法拿不到这个startTime变量.
但是这是就会存在线程安全问题:
出现线程安全问题的前提条件:
1) 多个线程并发执行;
2) 多个线程存在共享数据
3) 多个线程对共享数据的操作不是原子操作.
这个事件拦截器是单例的,但是可能需要处理多个请求,startTime定义在全局变量,那么这个数据就是共享的,
当请求1需要打印所用时间是,但是得到的startTime不是这个请求对应的startTime,可能是其他线程更改了之后的时间.
对于这种情况,最好是不要使用共享数据,将数据都存在单独的request域中
然后需要用到这个数据时,就从自己的request域中拿这个数据,那么这样就不会出现线程安全问题.