spring項目--過濾器,攔截器,監聽器

一、http請求後臺執行的順序

啓動順序:監聽器 > 過濾器 > 攔截器(context-param-->listener-->filter-->servlet-->interceptor)

記憶技巧:接到命令,監聽電報,過濾敵情,攔截行動。

二、區別和理解

如果我們現在大海就是我們啓動的項目,那麼監聽器就能夠聽到整個大海的聲音,過濾器就是能夠過濾出其中的魚,而攔截器則是攔截其中的部分魚,並且作標記。所以當需要監聽到項目中的一些信息,並且不需要對流程做更改時,用監聽器;當需要過濾掉其中的部分信息,只留一部分時,就用過濾器;當需要對其流程進行更改,做相關的記錄時用攔截器。

三、詳細概念

context-param:就是一些需要初始化的配置,放入context-param中,從而被監聽器(這裏特指org.springframework.web.context.ContextLoaderListener)監聽,然後加載;

監聽器(listener):就是對項目起到監聽的作用,它能感知到包括request(請求域),session(會話域)和applicaiton(應用程序)的初始化和屬性的變化;

過濾器(filter):就是對請求起到過濾的作用,它在監聽器之後,作用在servlet之前,對請求進行過濾;

servlet:就是對request和response進行處理的容器,它在filter之後執行,servlet其中的一部分就是controller層(標記爲servlet_2),還包括渲染視圖層(標記爲servlet_3)和進入controller之前系統的一些處理部分(servlet_1),另外我們把servlet開始的時刻標記爲servlet_0,servlet結束的時刻標記爲servlet_4。

攔截器(interceptor):就是對請求和返回進行攔截,它作用在servlet的內部,具體來說有三個地方:

1)servlet_1和servlet_2之間,即請求還沒有到controller層

2)servlet_2和servlet_3之間,即請求走出controller層次,還沒有到渲染時圖層

3)servlet_3和servlet_4之間,即結束視圖渲染,但是還沒有到servlet的結束

它們之間的關係,可以用一張圖來表示:

四、用途

監聽器的用途:

1.通常使用Web監聽器做以下的內容:
  2.統計在線人數,利用HttpSessionLisener
  3.加載初始化信息:利用ServletContextListener
  4.統計網站訪問量
  5.實現訪問監控

servlet的監聽接口,
1)ServletContextListener監聽web應用的啓動和關閉
2)ServletContextAttributeListener監聽ServletContext範圍內屬性的改變
3)ServletRequestListener監聽用戶的請求
4)ServletRequestAttributeListener監聽ServletRequest範圍內(request)內屬性的變化
5)HttpSessionListener監聽用戶session的開始和結束
6)HttpSessionAttributeListener監聽HttpSession範圍內session內屬性的改變。
springmvc的監聽器ContextLoaderListener(實現ServletContextListener)用於加載spring容器的配置文件。
 

過濾器的用途:

1、對請求參數request,response等設置字符編碼或者其他響應頭

2、對用戶登錄信息獲取,沒有重定向到登錄頁,實現用戶權限訪問

3、對一些非法請求過濾處理,如敏感參數等

攔截器:

對通過過濾的指定攔截的請求,做一些特殊處理,如aop的模式(性能分析, 權限檢查, 日誌記錄)

五、java代碼實現

過濾器:

定義一個TimeFilter類,實現javax.servlet.Filter:
public class TimeFilter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("過濾器初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("開始執行過濾器");
        Long start = new Date().getTime();
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("【過濾器】耗時 " + (new Date().getTime() - start));
        System.out.println("結束執行過濾器");
    }

    @Override
    public void destroy() {
        System.out.println("過濾器銷燬");
    }
}
TimeFilter重寫了Filter的三個方法,方法名稱已經很直白的描述了其作用,這裏不再贅述。要使該過濾器在Spring Boot中生效,還需要一些配置。這裏主要有兩種配置方式。

配置方式一
可通過在TimeFilter上加上如下註解:
@Component
@WebFilter(urlPatterns = {"/*"})
public class TimeFilter implements Filter {
   ...
}

@Component註解讓TimeFilter成爲Spring上下文中的一個Bean,@WebFilter註解的urlPatterns屬性配置了哪些請求可以進入該過濾器,/*表示所有請求。

配置方式二
除了在過濾器類上加註解外,我們也可以通過FilterRegistrationBean來註冊過濾器。

定義一個WebConfig類,加上@Configuration註解表明其爲配置類,然後通過FilterRegistrationBean來註冊過濾器:
@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean timeFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        TimeFilter timeFilter = new TimeFilter();
        filterRegistrationBean.setFilter(timeFilter);

        List<String> urlList = new ArrayList<>();
        urlList.add("/*");

        filterRegistrationBean.setUrlPatterns(urlList);
        return filterRegistrationBean;
    }
}
 

FilterRegistrationBean除了註冊過濾器TimeFilter外還通過setUrlPatterns方法配置了URL匹配規則。重啓項目訪問

通過過濾器我們只可以獲取到servletRequest對象,所以並不能獲取到方法的名稱,所屬類,參數等額外的信息。

攔截器:

定義一個TimeInterceptor類,實現org.springframework.web.servlet.HandlerInterceptor接口:
public class TimeInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("處理攔截之前");
        httpServletRequest.setAttribute("startTime", new Date().getTime());
        System.out.println(((HandlerMethod) o).getBean().getClass().getName());
        System.out.println(((HandlerMethod) o).getMethod().getName());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("開始處理攔截");
        Long start = (Long) httpServletRequest.getAttribute("startTime");
        System.out.println("【攔截器】耗時 " + (new Date().getTime() - start));
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("處理攔截之後");
        Long start = (Long) httpServletRequest.getAttribute("startTime");
        System.out.println("【攔截器】耗時 " + (new Date().getTime() - start));
        System.out.println("異常信息 " + e);
    }
}

TimeInterceptor實現了HandlerInterceptor接口的三個方法。preHandle方法在處理攔截之前執行,postHandle只有當被攔截的方法沒有拋出異常成功時纔會處理,afterCompletion方法無論被攔截的方法拋出異常與否都會執行。

通過這三個方法的參數可以看到,相較於過濾器,攔截器多了Object和Exception對象,所以可以獲取的信息比過濾器要多的多。但過濾器仍無法獲取到方法的參數等信息,我們可以通過切面編程來實現這個目的,具體可參考https://mrbird.cc/Spring-Boot-AOP%20log.html。

要使攔截器在Spring Boot中生效,還需要如下兩步配置:

1.在攔截器類上加入@Component註解;

2.在WebConfig中通過InterceptorRegistry註冊過濾器:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Autowired
    private TimeInterceptor timeInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor);
    }
}
監聽器:

第一種:在初始化的時候添加監聽器 app.addListeners(new MyApplicationListener());

第二種:

第三種:在application.propertiies 添加

context.listener.classes=com.boot.enable.addlistener.MyApplicationListener

第四種:

@Component
public class HandlerEvent {

    @EventListener
    public void evendListener(MyApplicationEvent event){
        System.out.println("監聽了-----"+event.getSource());
        System.out.println("監聽了-----"+event.getClass());
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章