当前位置: 首页 > 科技观察

过滤器Filter与拦截器Interceptor的联系与区别

时间:2023-03-20 22:20:35 科技观察

本文将对拦截器Interceptor进行简单的讲解,并通过几个例子来简单分析一下它们的区别。拦截器介绍一个应用程序中可以定义多个拦截器,spring会在项目启动时注册这些拦截器,并按照默认的规则进行排序。如果是自定义拦截器,可以手动设置拦截器调用顺序。每个拦截器都是一个链式调用。一个请求可以触发多个拦截器,每个拦截器的调用会按照加载到spring中的顺序依次执行。拦截器中有3个方法,作用如下:preHandle:该方法在调用Controller方法或获取静态资源(静态资源包括html、js等)之前调用。postHandle:该方法在调用Controller方法或获取静态资源之后,视图渲染之前被调用。afterCompletion:该方法在视图渲染完成后调用,主要用于清除资源。自定义拦截器InterceptorpublicclassMyInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{System.out.println("调用preHandle");returntrue;}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{System.out.println("CallpostHandle");}@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{System.out.println("CallafterCompletion");}}注册自定义拦截器Interceptor编写配置类,实现WebMvcConfigurer接口。@ConfigurationpublicclassMyWebConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newMyInterceptor()).addPathPatterns("/**");}}FilterFilter和Interceptor拦截器区别Filter和拦截器可以实现如编码设置,日志记录、权限控制等功能,但两者还是有很多区别的。(1)实现原理不同Filter是基于函数回调实现的:每个自定义的filter都会实现一个doFilter()方法,这个方法有一个关键参数FilterChain。它是一个回调接口,ApplicationFilterChain是它的具体实现类。该类内部还有一个doFilter()方法,是回调方法(ps:可以理解为方法递归调用,查看源码更容易理解)。假设有2个过滤器,调用流程图如下(ps:画得比较抽象):两个过滤器的执行过程Interceptorisimplementedbasedonreflection:为什么拦截器是基于反射实现的?个人理解在拦截器里面,三个方法中,有一个公共的参数handler,里面包含了丰富的信息。包含请求对应的方法、方法所在的Controller、方法参数等信息,这些都是spring通过反射加载的。正是因为有这些丰富的参数,拦截功能才比过滤功能更加强大。Handler参数信息(2)不同作用域的过滤器用于实现javax.servlet.Filter接口,也就是说过滤器的使用依赖于Tomcat等容器,所以只能在web程序中使用。拦截器实现了org.springframework.web.servlet接口。由Spring容器管理,不依赖Tomcat等容器。它可以应用于网络程序和非网络程序。(3)触发时机不同。过滤器在请求进入Tomcat等容器之后,servlet处理之前被调用。拦截器Interceptor在请求进入servlet之后,执行Controller之前被调用。(4)拦截范围不同。Filter几乎可以拦截所有进入容器的请求。拦截器Interceptor只会作用于Controller请求或者访问静态目录下的静态资源的请求。(5)初始化时机不同Filter是随着Tomcat等web容器的启动而初始化的。拦截器Interceptor是用springstartup初始化的。过滤器和拦截器如何注入依赖服务在实际开发中,使用过滤器或拦截器时,不可避免地会引入一些依赖服务。下面通过一个例子简单说明一下:Filter依赖服务:直接使用注解@Autowired即可。@WebFilter(urlPatterns={"/user/*"})@Log4j2publicclassMyFilterimplementsFilter{@AutowiredprivateUserServiceuserService;@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainfilterChain)throwsIOException,ServletException{HttpServletRequesthttpServletRequest=(HttpServletRequest)request;log.info("Filter获取到请求地址:"+httpServletRequest.getServletPath());filterChain.doFilter(request,response);HttpServletResponsehttpServletResponse=(HttpServletResponse)response;log.info("Filter得到响应类型:"+httpServletResponse.getContentTypever());}@OidlicFilterConfigfilterConfig)throwsServletException{System.out.println("Filter随着项目的启动而启动,只初始化一次");}@Overridepublicvoiddestroy(){System.out.println("Filter随着web项目的停止而销毁,完成资源回收");}}拦截器依赖服务:直接使用注解@Autowired,但是在将拦截器注入spring容器时,不能自己通过new创建的。拦截器需要像普通bean一样注入到spring容器中,这样才能将服务注入到拦截器中。publicclassMyInterceptorimplementsHandlerInterceptor{@AutowiredprivateUserServiceuserService;@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{System.out.println("调用preHandle");returntrue;}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{System.out.println("调用postHandle");}@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{System.out.println("调用afterCompletion");}}@ConfigurationpublicclassMyWebConfigimplementsWebMvcConfigurer{@BeanpublicMyInterceptorgetMyInterceptor(){returnnew@MyOverInterceptor()}public;addMyOverInterceptor()sInterceptorRegistryregistry){//getMyInterceptor()这个注册方法可以注入beanregistry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");//这个注册方法是由于自身neW出来了,所以拦截器里注册的bean都是nullregistry.addInterceptor(newMyInterceptor2()).addPathPatterns("/**");}}如何指定过滤器和拦截器的加载顺序(一)过滤器:需要通过配置类指定加载顺序。值越小,执行越早。@WebFilter无法指定顺序。@BeanpublicFilterRegistrationBeanmyFilter(){MyFiltermyFilter=newMyFilter();FilterRegistrationBeanfilterRegistrationBean=newFilterRegistrationBean(myFilter);filterRegistrationBean.setUrlPatterns(Arrays.asList("/user/*"));filterRegistrationBean.setOrder(2);Regiturnfilter}加载顺序需要通过配置类指定,值越小越早执行。@ConfigurationpublicclassMyWebConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){//getMyInterceptor()这个注册方法可以注入beanregistry.addInterceptor(newMyInterceptor()).addPathPatterns("/**").order(1);//这个由于这个注册方法是自己new,拦截器中注册的bean都是nullregistry.addInterceptor(newMyInterceptor2()).addPathPatterns("/**").order(2);}}filter过滤和拦截拦截器的使用场景比拦截器。它能做Filter能做的事,可以在请求之前或之后执行,更加灵活。Filter主要用于设置字符编码,过滤敏感词和简单的URL级别的权限控制。如果需要记录更详细的信息或者更复杂的权限管理,建议使用拦截器。扩展Servlet和Controller有什么区别?使用Servlet可以从Web表单收集用户输入并动态创建网页。DispatcherServlet是SpringMVC中唯一的Servlet。Servlet容器(Tomcat)将所有请求转发给DispatcherServlet,再通过HandlerMapping将请求路由到具体的Controller。因此,Controller就是一个普通的JavaBean。