作者:liuxiaopeng
鏈接:http://www.cnblogs.com/paddix
作者:藍精靈lx
原文:https://blog.csdn.net/liuxiao723846/article/details/80656492
參考以上兩位作者文章鏈接進行實驗整合,僅供學習交流
一、攔截器與過濾器
先理解一下AOP的概念,AOP不是一種具體的技術,而是一種編程思想。
在面向對象編程的過程中,我們很容易通過繼承、多態來解決縱向擴展。
但是對於橫向的功能,比如,在所有的service方法中開啟事務,或者統一記錄日志等功能,面向對象的是無法解決的。所以AOP——面向切面編程其實是面向對象編程思想的一個補充。而我們今天講的過濾器和攔截器都屬於面向切面編程的具體實現。
過濾器(Filter)與攔截器()主要區別包括以下幾個方面:
-
Filter是依賴於Servlet容器,屬於Servlet規范的一部分,而攔截器則是獨立存在的,可以在任何情況下使用。
-
Filter的執行由Servlet容器回調完成,而攔截器通常通過動態代理的方式來執行。
-
Filter的生命周期由Servlet容器管理,而攔截器則可以通過IoC容器來管理,因此可以通過注入等方式來獲取其他Bean的實例,使用更方便。
二、過濾器的實現
(1)自定義一個實現javax.servlet.Filter接口的過濾器類
package com.test.domain; import javax.servlet.*; import java.io.IOException; public class LogCostFilter implements Filter {
//servlet容器初始化時 @Override public void init(FilterConfig filterConfig) throws ServletException { }
//servlet容器存在時 @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { long start = System.currentTimeMillis(); filterChain.doFilter(servletRequest,servletResponse); System.out.println("Execute cost="+(System.currentTimeMillis()-start)); }
//servlet容器銷毀時 @Override public void destroy() { } }
這段代碼的邏輯比較簡單,就是在方法執行前先記錄時間戳,然后通過過濾器鏈完成請求的執行,在返回結果之間計算執行的時間。這里需要主要,這個類必須繼承Filter類。
可以看出,Filter的生命周期由Servlet容器相關
(2)在Spring boot中,我們需要FilterRegistrationBean來完成配置。其實現過程如下:
package com.test.config;
import com.test.domain.LogCostFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean registration(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new LogCostFilter());//實例化Filter類 filterRegistrationBean.addUrlPatterns("/*");//設置匹配模式,這里設置為所有,可以按需求設置為"/hello"等等 filterRegistrationBean.setName("LogCostFilter");//設置過濾器名稱 filterRegistrationBean.setOrder(1);//設置執行順序 return filterRegistrationBean; } }
啟動服務器訪問任意URL皆可得到打印的時間戳
過濾器的另一種實現方法:可以在啟動類上使用 @ServletComponentScan("com.test.domian.FilterCostConfig")和實現了Filter接口的自定義類上使用注解 @WebFilter(urlPatterns = "/*",filterName = "LogCostFilter"),不過@WebFilter這個注解是Servlet3.0的規范,且並沒有指定執行的順序,其執行順序依賴於Filter的名稱,是根據Filter類名(注意不是配置的filter的名字)的字母順序倒序排列,並且@WebFilter指定的過濾器優先級都高於FilterRegistrationBean配置的過濾器。
三、攔截器的實現
使用攔截器來實現上面同樣的功能,記錄請求的執行時間。
(1)自定義一個實現HandlerInterceptor 接口的攔截器類:
package com.test.interceptor; import org.springframework.lang.Nullable; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LogcostInterceptor implements HandlerInterceptor { long start = System.currentTimeMillis(); //preHandle是在請求執行前執行的 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { start = System.currentTimeMillis(); return true; //返回true,postHandler和afterCompletion方法才能執行 // 否則false為拒絕執行,起到攔截器控制作用 } //postHandler是在請求結束之后,視圖渲染之前執行的,但只有preHandle方法返回true的時候才會執行 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { System.out.println("Interception cost="+(System.currentTimeMillis()-start)); } //afterCompletion是視圖渲染完成之后才執行,同樣需要preHandle返回true, @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { //該方法通常用於清理資源等工作 } }
這里我們需要自定義一個類來實現HandlerInterceptor這個接口,實現里面的三個方法,具體說明已寫在方法注釋中
(2)攔截器類的配置:
package com.test.config; import com.test.interceptor.LogcostInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; @Configuration public class InterceptorConfig extends WebMvcConfigurationSupport { /** * 靜態資源配置 */ /*@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/img/**") .addResourceLocations("classpath:/imgs/"); super.addResourceHandlers(registry); }*/ /** * 默認首頁配置 */ // @Override // public void addViewControllers(ViewControllerRegistry registry) { // registry.addViewController("/").setViewName("forward:/index"); // registry.setOrder(Ordered.HIGHEST_PRECEDENCE); // super.addViewControllers(registry); // } /** * interceptor配置 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogcostInterceptor()) //添加需要驗證登錄用戶操作權限的請求 .addPathPatterns("/**") //這里add為“/**”,下面的exclude才起作用,且不管controller層是否有匹配客戶端請求,攔截器都起作用攔截 // .addPathPatterns("/hello") //如果add為具體的匹配如“/hello”,下面的exclude不起作用,且controller層不匹配客戶端請求時攔截器不起作用 //排除不需要驗證登錄用戶操作權限的請求 .excludePathPatterns("/wang") .excludePathPatterns("/css/**") .excludePathPatterns("/js/**") .excludePathPatterns("/images/**"); //這里可以用registry.addInterceptor添加多個攔截器實例,后面加上匹配模式 super.addInterceptors(registry);//最后將register往這里塞進去就可以了 } }
這里我們重寫了addInterceptors這個方法,進行攔截器的配置,主要配置項就兩個,一個是指定攔截器(定義方法行為),第二個是指定攔截的URL(模式匹配)。
注釋部分的靜態資源配置和默認首頁配置本人還未做實驗,應該沒問題的
再啟動系統訪問任意一個URL即可得到打印的時間戳
現在我們權限和認證更多的是在Spring Boot中整合使用shiro或者Spring Security來完成,比較少用自定義攔截器了,不過可以用攔截器實現其他功能,比如ip黑名單等等特殊場景