目录
一、过滤器Filter
-
创建过滤器
- @Component注解,实现Filter接口,重写init()、doFilter()和destroy()方法
@Component public class MyFilter1 implements Filter { /** * 容器初始化时执行过滤器初始化方法 * @param filterConfig * @throws ServletException */ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("执行MyFilter1初始化方法init()。。。"); } /** * 每次发起请求后,在Controller方法前执行 * @param servletRequest * @param servletResponse * @param filterChain * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("执行MyFilter1过滤器方法doFilter()..."); filterChain.doFilter(servletRequest,servletResponse); } /** * 容器销毁时执行销毁过滤器方法 */ @Override public void destroy() { System.out.println("执行MyFilter1销毁方法destroy()。。。"); } }
- @Component注解,实现Filter接口,重写init()、doFilter()和destroy()方法
-
过滤器执行
- 容器初始化时,会且仅有一次执行init()方法
- 每次发起被拦截的请求时,都会调用doFilter()方法,然后通过调用其中的FilterChain回调接口的doFilter()回调方法,去依次调用其他过滤器的处理方法(doFilter())
- 容器销毁时,会且仅有一次执行过滤器销毁方法(destroy())
-
实现原理
- 过滤器的底层时基于函数回调实现的
- 我们自定义的过滤器,都会实现其处理方法doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain),其中的FilterChain类型参数,它实际上是一个回调接口,而ApplicationFilterChain即是它的一个实现类
- 因此执行自定义过滤器中的doFilter()方法中的filterChain.doFilter(servletRequest,servletResponse),即是执行ApplicationFilterChain中的doFilter(servletRequest,servletResponse)方法,该方法会获取容器中的所有过滤器,并依次执行各自的doFilter()方法,从而实现所有过滤器的执行
- 我们自定义的过滤器,都会实现其处理方法doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain),其中的FilterChain类型参数,它实际上是一个回调接口,而ApplicationFilterChain即是它的一个实现类
- 过滤器的底层时基于函数回调实现的
-
应用场景
- 过滤器只能在web应用中使用
- 过滤器实现的是javax.servlet.Filter接口,即依赖于类似tomcat等servlet容器
- 过滤器实现的是javax.servlet.Filter接口,即依赖于类似tomcat等servlet容器
- 过滤器只能在web应用中使用
二、拦截器Interceptor
-
创建过滤器
- @Component注解,实现HandlerInterceptor接口,重写preHandle()、postHandle()和afterCompletion()方法
@Component public class MyInterceptor1 implements HandlerInterceptor { /** * 每次发起请求后,在Controller方法前执行过滤器方法 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("执行拦截器MyInterceptor1方法preHandle()..."); response.setHeader("sss","preHandle"); return true; } /** * preHandle()方法返回true时,在Controller方法调用后,DispatcherServlet返回渲染视图后执行 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("执行拦截器MyInterceptor1方法postHandle()..."); response.setHeader("ddd","postHandle"); if (null != modelAndView){ System.out.println("view name------"+modelAndView.getViewName()); modelAndView.addObject("kkk","postHandle"); } } /** * preHandle()方法返回true时,在Controller方法调用后,DispatcherServlet返回渲染视图后执行 * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("执行拦截器MyInterceptor1的处理后方法afterCompletion()..."); response.setHeader("hhh","afterCompletion"); } }
- 并需要在新建一个mvc配置文件(@Configuration注解,实现WebMvcConfigurer接口),在配置文件中注册拦截器(重写addInterceptors()方法)
@Configuration public class MyMvcConfig implements WebMvcConfigurer { /** * 注册拦截器,并添加待拦截的请求路径 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { System.out.println("开始注册拦截器。。。"); registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**"); registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**"); System.out.println("结束注册拦截器。。。"); } }
- 此时要注意,在注册拦截器时,如果是使用上面方法,直接new的拦截器实例(如new MyInterceptor1()),即意味着该实例不会被spring管理,则类MyInterceptor1中注入的其他bean或service组件都会是null
- 要解决这个问题,就应该在注册拦截器时,通过依赖注入的方式,将拦截器实例交给spring管理,进而完成service等组件的注入;而非自己new
@Configuration public class MyMvcConfig implements WebMvcConfigurer { /** * 向容器中注入自定义拦截器 * @return */ // @Bean // public MyInterceptor1 myInterceptor1(){ // System.out.println("向容器中注入拦截器myInterceptor1实例..."); // return new MyInterceptor1(); // } @Autowired MyInterceptor1 myInterceptor1; @Autowired MyInterceptor2 myInterceptor2; /** * 注册拦截器,并添加待拦截的请求路径 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { System.out.println("开始注册拦截器。。。"); registry.addInterceptor(myInterceptor1).addPathPatterns("/**"); // registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**"); registry.addInterceptor(myInterceptor2).addPathPatterns("/**"); // registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**"); System.out.println("结束注册拦截器。。。"); } }
- 此时要注意,在注册拦截器时,如果是使用上面方法,直接new的拦截器实例(如new MyInterceptor1()),即意味着该实例不会被spring管理,则类MyInterceptor1中注入的其他bean或service组件都会是null
- @Component注解,实现HandlerInterceptor接口,重写preHandle()、postHandle()和afterCompletion()方法
-
过滤器执行
- 容器初始化时,会通过配置文件,注册拦截器
- 每次发起被拦截的请求时,首先执行过滤器处理方法(doFilter()),然后按注册顺序执行拦截器处理方法(preHandler()),再执行controller方法,再按注册倒序执行拦截器方法postHandler(),最后按注册倒序执行拦截器销毁方法(afterCompletion())
@Controller @RequestMapping("/") @ResponseBody public class HelloWorld { /** * Controller方法 * @return */ @RequestMapping("/hello") public String hello(){ System.out.println("我是controller!"); return "Hello World!"; } }
- preHandle()方法调用后,如果返回值是false,则视为当前请求结束,不仅自身会失效,后面的拦截器也会失效
- postHandle()方法只会在preHandle()方法返回true后执行,具体是在Controller方法调用后,DispatcherServlet返回渲染视图之前调用
- afterCompetion()方法只会在preHandle()方法返回true后执行,具体是在整个请求结束之后,DispatcherServlet返回渲染视图之后调用
-
实现原理
- 拦截器是基于Java的反射机制(动态代理)实现的
-
应用场景
- 拦截器不仅能应用在web程序中,也可用于Application、Swing等程序中
- 拦截器实现的是org.springframework.web.servlet.HandlerInterceptor接口,它是一个spring组件,故可不依赖于tomcat等servlet容器而单独使用
- 拦截器实现的是org.springframework.web.servlet.HandlerInterceptor接口,它是一个spring组件,故可不依赖于tomcat等servlet容器而单独使用
- 拦截器不仅能应用在web程序中,也可用于Application、Swing等程序中
三、过滤器和拦截器的其他区别
-
触发时机的不同
- 过滤器Filter是在请求进入容器后,servlet之前就可触发;在servlet处理完,容器销毁前结束后处理destroy();而拦截器Interceptor是在请求进入servlet之后,Controller方法之前触发;在DispatcherServlet返回渲染视图之后结束后处理afterCompetion()
- 过滤器Filter是在请求进入容器后,servlet之前就可触发;在servlet处理完,容器销毁前结束后处理destroy();而拦截器Interceptor是在请求进入servlet之后,Controller方法之前触发;在DispatcherServlet返回渲染视图之后结束后处理afterCompetion()
-
作用范围不同
- 基于各自实现原理和触发时机的不同,导致过滤器Filter只在servlet前后起作用,它对几乎所有请求可起作用;而拦截器Interceptor可深入到方法前后、异常抛出前后,对请求起作用,也可访问上下文、栈里的对象等;因此在spring框架中,可优先使用拦截器。