在後端項目開發中,會有一些須要基於全局處理的程序。傳統基於Servlet容器的程序中,咱們可使用過濾器和監聽器,在Java 框架中還可使用攔截器,而面向切面編程AOP更是做爲Spring框架中的核心思想被你們所關注。本文一方面從概念上講解Filter、Listener、Interceptor和aop的區別,另外一方面更是從代碼層面講解如何在SpringBoot中應用開發。java
它們的執行順序以下(@ControllerAdvice本文暫不作介紹):web
攔截順序:ServletContextListener> Filter > Interception > AOP > 具體執行的方法 > AOP > @ControllerAdvice > Interception > Filter > ServletContextListenerredis
根據實現原理分紅下面兩大類:spring
從 Filter -> Interceptor -> aop ,攔截的功能愈來愈細緻、強大,尤爲是Interceptor和aop能夠更好的結合spring框架的上下文進行開發。可是攔截順序也是愈來愈靠後,請求是先進入Servlet容器的,越早的過濾和攔截對系統性能的消耗越少。具體選用哪一種方法,就須要開發人員根據實際業務狀況綜合考慮了。數據庫
Filter過濾器是Servlet容器層面的,在實現上基於函數回調,能夠對幾乎全部請求進行過濾。過濾器是對數據進行過濾,預處理過程,當咱們訪問網站時,有時候會發布一些敏感信息,發完之後有的會用*替代,還有就是登錄權限控制等,一個資源,沒有通過受權,確定是不能讓用戶隨便訪問的,這個時候,也能夠用到過濾器。過濾器的功能還有不少,例如實現URL級別的權限控制、壓縮響應信息、編碼格式等等。編程
SpringBoot實現過濾器,常見有三種方式,越複雜功能越強大。後端
這種方式最簡單,直接實現Filter接口,並使用@Component註解標註爲組件自動注入bean。可是缺點是沒辦法設置過濾的路徑,默認是 /* 過濾全部。緩存
Filter接口有 init、doFilter、destroy 三個方法,但 init、destroy 是有默認方法實現,能夠不重寫。服務器
import org.springframework.stereotype.Component; import javax.servlet.*; import java.io.IOException; @Component public class DemoFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException{ HttpServletRequest httpServletRequest=(HttpServletRequest)servletRequest; HttpServletResponse httpServletResponse=(HttpServletResponse)servletResponse; System.out.println("filter1----------"+httpServletResponse.getStatus()); filterChain.doFilter(servletRequest,servletResponse); } }
這種方式要稍微複雜一點,但更全面。使用 @WebFilter替代 @Component,能夠經過該註解設置過濾器的匹配路徑。不過須要在啓動類中使用@ServletComponentScan。@ServletComponentScan掃描帶@WebFilter、@WebServlet、@WebListener並將幫咱們注入bean。session
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter(filterName = "filter1",urlPatterns = {"/hello/*"}) public class DemoFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException{ HttpServletRequest httpServletRequest=(HttpServletRequest)servletRequest; HttpServletResponse httpServletResponse=(HttpServletResponse)servletResponse; System.out.println("filter1----------"+httpServletResponse.getStatus()); filterChain.doFilter(servletRequest,servletResponse); } }
這種方式徹底經過配置類來實現,在只實現過濾器的接口,並不須要經過任何註解注入IOC容器,都經過配置類來注入。
AFilter.java(BFilter.java也相似)
import javax.servlet.*; import java.io.IOException; public class AFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterA----------"); filterChain.doFilter(servletRequest,servletResponse); } }
FilterConfig.java 配置類
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import pers.kerry.demo.webfilter.filter.AFilter; import pers.kerry.demo.webfilter.filter.BFilter; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean AFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); AFilter aFilter=new AFilter(); filterRegistrationBean.setFilter(aFilter); filterRegistrationBean.addUrlPatterns("*"); filterRegistrationBean.setName("AFilter"); filterRegistrationBean.setOrder(1); return filterRegistrationBean; } @Bean public FilterRegistrationBean BFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); BFilter bFilter=new BFilter(); filterRegistrationBean.setFilter(bFilter); filterRegistrationBean.addUrlPatterns("*"); filterRegistrationBean.setName("BFilter"); filterRegistrationBean.setOrder(2); return filterRegistrationBean; } }
Listener監聽器也是Servlet層面的,能夠用於監聽Web應用中某些對象、信息的建立、銷燬和修改等動做發生,而後作出相應的響應處理。根據監聽對象,將監聽器分爲3類:
在寫Listener的類時,實現方式和Filter同樣,一樣有兩種實時方式。一種是隻加@Component;另外一種是 @WebListener 和 @ServletComponentScan 配合使用。不過實現接口則根據監聽對象區分,如:ServletContextListener、HttpSessionListener和ServletRequestListener。
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class DemoListener implements ServletContextListener{ @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContextListener 初始化上下文"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContextListener 銷燬"); } }
Interceptor攔截器和Filter和Listener有本質上的不一樣,前面兩者都是依賴於Servlet容器,而Interceptor則是依賴於Spring框架,是aop的一種表現,基於Java的動態代理實現的。在SpringBoot中實現攔截器的方式,有點相似於實現過濾器的第三種方式,因此要經過下面兩個步驟。
DemoInterceptor.java
import org.springframework.lang.Nullable; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; public class DemoInterceptor implements HandlerInterceptor{ /** * 預處理回調方法,實現處理器的預處理(如檢查登錄),第三個參數爲響應的處理器 * 返回值:true表示繼續流程(如調用下一個攔截器或處理器);false表示流程中斷(如登陸檢查失敗),不會繼續調用其餘的攔截器或處理器,此時咱們須要經過response來產生響應 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); String methodName = method.getName(); System.out.println("====攔截到了方法:"+methodName+",preHandle===="); return true; } /** * 後處理回調方法,實現處理器的後處理(但在渲染視圖以前),此時咱們能夠經過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能爲null */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { System.out.println("====postHandle===="); } /** *整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中咱們能夠在此記錄結束時間並輸出消耗時間,還能夠進行一些資源清理,相似於try-catch-finally中的finally,但僅調用處理器執行鏈中 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { System.out.println("====afterCompletion===="); } }
InterceptorConfig.java
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import pers.kerry.demo.webfilter.interceptor.DemoInterceptor; /** * spring 2.x 之前,經過繼承 WebMvcConfigurerAdapter 類 * spring 2.x 以後,實現 WebMvcConfigurer 接口 */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**"); } }
相比較於攔截器,Spring 的aop則功能更強大,封裝的更細緻,須要單獨引用 jar包。
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
在定義AOP的類時,不須要和前面攔截器同樣麻煩了,只須要經過註解,底層實現邏輯都經過IOC框架實現好了,涉及到的註解以下:
DemoAspect.java
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @Aspect 註解 使之成爲切面類 * @Component 註解 把切面類加入到IOC容器中 */ @Aspect @Component public class DemoAspect { @Pointcut( "execution( public * pers.kerry.demo.webfilter.controller.DemoController.*(..) )") public void doPointcut(){ } @Before("doPointcut()") public void doBefore(){ System.out.println("==doBefore=="); } @After("doPointcut()") public void doAfter(){ System.out.println("==doAfter=="); } @AfterReturning("doPointcut()") public void doAfterReturning(){ System.out.println("==doAfterReturning=="); } /** * 返回值類型Object,對應全部被攔截方法的返回值類型,不能爲void */ @Around("doPointcut()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{ System.out.println("==doAround.before=="); Object ret=proceedingJoinPoint.proceed(); System.out.println("==doAround.after=="); return ret; } }