欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 前端技术 > javascript >内容正文

javascript

SpringMVC中过滤器和拦截器的区别

发布时间:2025/3/19 javascript 36 豆豆
生活随笔 收集整理的这篇文章主要介绍了 SpringMVC中过滤器和拦截器的区别 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

文章目录

        • 1、引言
        • 2、共同点
        • 3、区别
          • 3.1、拦截器
          • 3.2、过滤器
        • 4、具体实现
          • 4.1、拦截器
            • 4.1.1、拦截实现方式
            • 4.1.2、拦截实现
          • 4.2、过滤器
            • 4.2.1、过滤器实现方式
            • 4.2.2、过滤器实现
        • 5、过滤器中依赖注入(深坑)
          • 5.1、问题描述
          • 5.2、Filter依赖注入实现
            • 5.2.1、方法一:web.xml配置实现
            • 5.2.2、方法二:继承WebApplicationInitializer,并注册Filter
            • 5.2.3、方法三:实现ApplicationContextAware接口的工具
            • 5.2.4、方法四:获取WebApplicationContext对象
        • 5、拦截器执行顺序

1、引言

我们在开发过程中都会遇到需要统一处理接口或者参数的场景,这个时候我们就会用到过滤器(Filter)或者拦截器(Intercepter)。
测试代码参考 chapter-2-springmvc-quickstart:
https://gitee.com/leo825/spring-framework-learning-example.git

2、共同点

1、都可以拦截请求和过滤请求
2、都用了责任链设计模式,并且都可以对请求进行预处理和后处理

3、区别

3.1、拦截器
  • 依赖于web框架实现,在我们使用的SpringMVC这种就是依赖于SpringMVC框架
  • 在实现上基于Java的反射机制,属于面向切面(AOP)的一种应用
  • 可以在一个Controller生命周期内进行多次调用,但是只能对Controller进行拦截
  • 主要作用:由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,并且一个拦截器可以在Controller生命周期内进行多次调用。
3.2、过滤器
  • 过滤器依赖于Servlet容器
  • 过滤器实现上基于函数回调,可以几乎对所有请求进行过滤(包括静态资源过滤)
  • 过滤器实例只能在容器初始化的时候调用一次
  • 主要作用:执行过滤操作,比如敏感信息、特殊请求、xss方漏洞、统一加解密参数等

4、具体实现

4.1、拦截器
4.1.1、拦截实现方式

SpringMVC拦截器(Interceptor)实现对每一个请求处理前后进行相关业务是通过HandlerInterceptor来实现的。定义一个拦截器,可以通过以下3种方式:

  • 实现Spring提供的的HandlerInterceptor接口;
  • 继承Spring中的抽象类HandlerInterceptorAdapter,此抽象类实现了HandlerInterceptor接口;
  • 实现Spring提供的WebRequestInterceptor接口;
  • 4.1.2、拦截实现

    具体实现举一个例子,测试代码如下:

    package com.leo.interceptor;import com.leo.service.UserInfoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.List;/*** @ClassName: HandlerInterceptor2* @Description: 测试拦截器2* 简单说明一下:本工程是采用xml方式注册的拦截器,因此可以直接依赖注入,* 如果是SpringBoot方式注册拦截器,不要使用new的方式创建拦截器对象,* 要把拦截器通过@Autowired注入进来,然后注册到Spring容器中,* 不然这个拦截器中通过依赖注入到的userInfoService永远是null* @Author: leo825* @Date: 2020-02-03 16:05* @Version: 1.0*/ public class HandlerInterceptor2 extends HandlerInterceptorAdapter {@AutowiredUserInfoService userInfoService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("HandlerInterceptor2 preHandle");HttpSession session = request.getSession();session.setAttribute("startTime",System.currentTimeMillis());return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("HandlerInterceptor2 postHandle");//访问数据库List userInfoList = userInfoService.getUserInfoList();System.out.print("HandlerInterceptor2 信息: ");System.out.println(userInfoList);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("HandlerInterceptor2 afterCompletion");HttpSession session = request.getSession();long startTime = (long)session.getAttribute("startTime");System.out.println("HandlerInterceptor2 过滤的接口耗时:" + (System.currentTimeMillis() - startTime) + "ms");} }

    spring配置文件要添加相应的配置,如下所示:

    <mvc:interceptor><!-- /* 是一级目录下的路径; /** 不分目录等级, 即所有请求 --><mvc:mapping path="/test2/**"/><bean class="com.leo.interceptor.HandlerInterceptor2"></bean></mvc:interceptor>

    访问地址:http://localhost:8080/springmvc/test2/hello,运行结果如下所示:

    HandlerInterceptor1 preHandle HandlerInterceptor2 preHandle HandlerInterceptor3 preHandle HandlerInterceptor4 preHandle HandlerInterceptor5 preHandle 使用配置实现 hello controller 跳转到 success HandlerInterceptor5 postHandle HandlerInterceptor4 postHandle HandlerInterceptor3 postHandle HandlerInterceptor2 postHandle HandlerInterceptor2 信息: [UserInfo{id=3, name='晓玲', gender='女', age='22', remarks='工程师'}, UserInfo{id=4, name='晓玲', gender='女', age='24', remarks='工程师'}] HandlerInterceptor1 postHandle HandlerInterceptor5 afterCompletion HandlerInterceptor5 过滤的接口耗时:330ms HandlerInterceptor4 afterCompletion HandlerInterceptor4 过滤的接口耗时:330ms HandlerInterceptor3 afterCompletion HandlerInterceptor3 过滤的接口耗时:330ms HandlerInterceptor2 afterCompletion HandlerInterceptor2 过滤的接口耗时:330ms HandlerInterceptor1 afterCompletion HandlerInterceptor1 过滤的接口耗时:330ms

    具体代码可以自行下载阅览。

    4.2、过滤器
    4.2.1、过滤器实现方式
  • 直接实现Filter接口,这一类过滤器只有CompositeFilter;
  • 继承抽象类GenericFilterBean,该类实现了Filter,这一类的过滤器只有一个,即DelegatingFilterProx;
  • 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
  • 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。
  • 这些类之间的关系就略了,可以参考这篇文章Spring MVC过滤器-超类

    4.2.2、过滤器实现

    过滤器的实现方法就举一个例子了,其他的可自行参考测试

    public class MyFilter3 extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {System.out.println("MyFilter3 执行过滤");filterChain.doFilter(httpServletRequest, httpServletResponse);} }

    还有莫忘了在web.xml中增加过滤器的拦截配置

    <filter><filter-name>MyFilter3</filter-name><filter-class>com.leo.filter.MyFilter3</filter-class></filter><filter-mapping><filter-name>MyFilter3</filter-name><url-pattern>/*</url-pattern></filter-mapping>

    还可以使用注解方式去实现例如在类上添加如下注解也可以实现

    @WebFilter(urlPatterns = "/*", filterName = "myFilter3")

    5、过滤器中依赖注入(深坑)

    5.1、问题描述

    在项目中需要在filter中注入@Service注解的Service服务,但是尝试了很多方法都无法实现(笔者也是),使用@Autowired注解注入的Service对象一致都是null,请问是这怎么回事?

    分析:既然是注入的Service对象一直是null,那就是考虑原因是否filter的创建要早于@Service注解的对象。
    尝试:如果是Filter比Service的Bean实例更早创建,那就改变创建的顺序,将@Service早一步创建不就行了。尝试之后仍然报null,那就不是这方面的问题了。

    再次从网上查阅资料,这块涉及web启动的原理,web应用启动的顺序是:Listener->Filter->Servlet,因为我们在web项目中一般都会用到两个配置文件applicationContext.xml和springmvc-servlet.xml,配置spring的时候会添加一个Listener,它会读取applicationContext.xm配置信息对Spring Context进行配置。因此在applicationContext.xml中的bean首先被初始化和注入,然后在对Filter进行初始化,在接着对DispatcherServlet进行初始化。因此我们在Filter中注入的Bean会失败。这里提供两种方式获取Bean

    5.2、Filter依赖注入实现
    5.2.1、方法一:web.xml配置实现

    通过配置代理,将自定义的Filter注入,配置代码如下:

    <filter><filter-name>DelegatingFilterProxy</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><init-param><param-name>targetBeanName</param-name><param-value>myFilter</param-value></init-param><init-param><param-name>targetFilterLifecycle</param-name><param-value>true</param-value><!-- 此参数必需设置--></init-param></filter><filter-mapping><filter-name>DelegatingFilterProxy</filter-name><url-pattern>/*</url-pattern></filter-mapping>

    过滤器的代码如下:

    @Component("myFilter") public class MyFilter implements Filter {@AutowiredUserInfoService userInfoService;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("MyFilter过滤器初始化");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("MyFilter过滤器执行过滤");//访问数据库List userInfoList = userInfoService.getUserInfoList();System.out.println(userInfoList);chain.doFilter(request, response);}@Overridepublic void destroy() {System.out.println("MyFilter过滤器销毁了");} }

    以上代码都是经过测试,真实可用的。

    5.2.2、方法二:继承WebApplicationInitializer,并注册Filter

    实现代码示例:

    public class MyFilterConfig implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy();//Bean实例名称String BeanId = "myFilter5";//代理的过滤器的BeandelegatingFilterProxy.setTargetBeanName(BeanId);//设置"targetFilterLifecycle"为True,则spring来管理Filter.init()和Filter.destroy();若为false,则这两个方法失效!delegatingFilterProxy.setTargetFilterLifecycle(true);//注册过滤器FilterRegistration filterRegistration = servletContext.addFilter(BeanId, delegatingFilterProxy);filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");System.out.println("代理拦截器,将Bean注入到Web容器中");} }

    其中的myFilter5就是过滤器的Bean实例,如下:

    @Component public class MyFilter5 implements Filter {@AutowiredUserInfoService userInfoService;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("MyFilter5 执行过滤");//访问数据库List userInfoList = userInfoService.getUserInfoList();System.out.print("MyFilter5 信息: ");System.out.println(userInfoList);chain.doFilter(request, response);}@Overridepublic void destroy() {} }
    5.2.3、方法三:实现ApplicationContextAware接口的工具

    首先编写ApplicationContextUtil工具类实现ApplicationContextAware接口

    @Component public class ApplicationContextUtil implements ApplicationContextAware{private static ApplicationContext applicationContext;/*** 通过bean的id获取bean对象* @param beanName* @return*/public static Object getBean(String beanName){return applicationContext.getBean(beanName);}/*** 根据bean的id和类型获取bean对象* @param beanName* @param clazz* @param <T>* @return*/public static <T> T getBean(String beanName,Class<T> clazz){return clazz.cast(getBean(beanName));}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;} }

    其次在拦截器中通过这个工具获取Bean对象

    @Component @WebFilter(urlPatterns = "/*", filterName = "myFilter6") public class MyFilter6 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("MyFilter6 执行过滤");//访问数据库,这个地方要注意使用的是“userInfoServiceImpl”,因为默认是按照类名首字符小写注入Spring中的UserInfoService userInfoService = (UserInfoService) ApplicationContextUtil.getBean("userInfoServiceImpl");List userInfoList = userInfoService.getUserInfoList();System.out.print("MyFilter6 信息: ");System.out.println(userInfoList);chain.doFilter(request, response);}@Overridepublic void destroy() {} }
    5.2.4、方法四:获取WebApplicationContext对象

    获取WebApplicationContext对象从而获取相应的Bean对象

    @Component @WebFilter(urlPatterns = "/*", filterName = "myFilter7") public class MyFilter7 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("MyFilter7 执行过滤");//访问数据库,这个地方要注意使用的是“userInfoServiceImpl”,因为默认是按照类名首字符小写注入Spring中的WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());UserInfoService userInfoService = (UserInfoService) webApplicationContext.getBean("userInfoServiceImpl");List userInfoList = userInfoService.getUserInfoList();System.out.print("MyFilter7 信息: ");System.out.println(userInfoList);chain.doFilter(request, response);}@Overridepublic void destroy() {} }

    通过WebApplicationContext获取Bean对象的方式有很多,上面只是举了一个普通的常见方式。
    例如:WebApplicationContextUtilsContextLoader方式
    可以参考《Spring容器中获取Bean实例的七种方式(附实战源码》这篇文章里面的示例。

    注意:平时都是使用 @Autowired按照类型注入UserInfoService,但是实际上Spring中注入的id=userInfoServiceImpl的Bean

    5、拦截器执行顺序

    拦截器和过滤器执行顺序可以根据下图来说明

    总结

    以上是生活随笔为你收集整理的SpringMVC中过滤器和拦截器的区别的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。