javascript
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種方式:
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、過濾器實現方式
這些類之間的關系就略了,可以參考這篇文章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對象的方式有很多,上面只是舉了一個普通的常見方式。
例如:WebApplicationContextUtils 和 ContextLoader方式
可以參考《Spring容器中獲取Bean實例的七種方式(附實戰源碼》這篇文章里面的示例。
注意:平時都是使用 @Autowired按照類型注入UserInfoService,但是實際上Spring中注入的id=userInfoServiceImpl的Bean
5、攔截器執行順序
攔截器和過濾器執行順序可以根據下圖來說明
總結
以上是生活随笔為你收集整理的SpringMVC中过滤器和拦截器的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring容器中获取Bean实例的七种
- 下一篇: SpringMVC学习(二)——快速搭建