什么是攔截器
攔截器(Interceptor): 用於在某個方法被訪問之前進行攔截,然后在方法執行之前或之后加入某些操作,其實就是AOP的一種實現策略。它通過動態攔截Action調用的對象,允許開發者定義在一個action執行的前后執行的代碼,也可以在一個action執行前阻止其執行。同時也是提供了一種可以提取action中可重用的部分的方式。
攔截器作用
攔截用戶的請求並進行相應的處理,比如:判斷用戶是否登陸,是否在可購買時間內,記錄日志信息等。
Spring中兩種實現方式
實現HandlerInterceptor接口
通過實現HandlerInterceptor接口, 一般通過繼承HandlerInterceptorAdapter抽象類實現。
DispatcherServlet處理流程:DispatcherServlet處理請求時會構造一個Excecution Chain,即(可能多個)攔截器和真正處理請求的Handler
即Interceptor是鏈式調用的。
preHandle: 在執行Handler之前進行,即Controller方法調用之前執行,主要進行初始化操作。
postHandle: 在執行Handler之后進行,即Controller 方法調用之后執行,主要對ModelAndView對象進行操作。
afterCompletion: 在整個請求結束之后,即渲染對應的視圖之后執行, 主要進行資源清理工作。
注意事項: 每個Interceptor的調用會依據它在xml文件中聲明順序依次執行。
DispatcherServlet中攔截器相關
實現WebRequestInterceptor接口
------------------------------------------------
SpringMVC的攔截器Interceptor和過濾器Filter功能非常相似,使用場景也差不多,看起來難以區分。比如兩者都能在代碼前后插入執行片段,都可以用來實現一些公共組件的功能復用(權限檢查、日志記錄等),其實它們並不一樣,首先了解一下Interceptor和Filter。
一.Interceptor
Interceptor是Spring攔截器,要實現一個攔截器功能可以繼承Spring的HandlerInterceptor接口:
package com.hpx.xiyou.wuKong.aop; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class sanZangInterceptor implements HandlerInterceptor{ static public final Logger logger = LoggerFactory.getLogger(sanZangInterceptor.class); @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { //System.out.println("interceptortest pre"); logger.info("interceptortest pre"); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { //System.out.println("interceptortest post"); logger.info("interceptortest post"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { //System.out.println("interceptortest after"); logger.info("interceptortest after"); } }
HandlerInterceptor接口有三個需要實現的方法:preHandle(),postHandle()和afterCompletion()。
preHandle方法將在請求處理之前調用,SpringMVC中的Interceptor是鏈式調用的,每個Interceptor的調用都根據它的聲明順序依次執行,且最先執行其preHandle方法,所以可以在該方法中進行一些前置初始化操作或是預處理。該方法的返回值是布爾類型,如果返回false,表示請求結束,后續的Interceptor和Controller都不會再執行了,如果返回true就執行下一個攔截器的preHandle方法,一直到最后一個攔截器preHandle方法執行完成后調用當前請求的Controller方法。
postHandle方法是在當前請求進行處理之后,也就是Controller方法調用結束之后執行,但是它會在DispatcherServlet進行視圖渲染之前被調用,所以可以在這個方法中可以對Controller處理之后的ModelAndView對象進行操作。postHandle方法被調用的方向跟preHandle是相反的,也就是說先聲明的Interceptor的postHandle方法反而后執行。
afterCompletion方法需要當前對應的Interceptor的preHandle方法的返回值為true時才會執行。該方法會在整個請求結束之后,也就是在DispatcherServlet渲染了對應的視圖之后執行,這個方法的主要作用是用於資源清理工作。
實現一個interceptor攔截器類后,需要在配置中配置使它生效:實現 WebMvcConfigurerAdapter並重寫 addInterceptors,同時在這個方法里設置要過濾的URL。
package com.hpx.xiyou.wuKong.Adapter; import com.hpx.xiyou.wuKong.aop.sanZangInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class WebConfigurerAdapter extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new sanZangInterceptor()).addPathPatterns("/wukong/**"); } }
以上配置生效后,當訪問/wukong/**類型url時,控制台輸出如下,其中controller為controller方法中的打印信息:
interceptortest pre
controller
interceptortest post
interceptortest after
二.Filter
Filter是Spring過濾器,要定義一個Filter類有以下步驟:
首先定義一個Filter類,繼承javax.servlet.Filter類,重寫其init、doFilter、destroy方法。init()方法會在Filter初始化后進行調用,在init()方法里面我們可以通過FilterConfig訪問到初始化參數( getInitParameter()或getInitParameters() )、ServletContext (getServletContext)和當前Filter部署的名稱( getFilterName() )等信息。destroy()方法將在Filter被銷毀之前調用。而doFilter()方法則是真正進行過濾處理的方法,在doFilter()方法內部,我們可以過濾請求的request和返回的response,同時我們還可以利用FilterChain把當前的request和response傳遞給下一個過濾器或Servlet進行處理。
public
class
FilterTest
implements
Filter {
@Autowired
private
PointService pointService;
@Override
public
void
init(FilterConfig filterConfig)
throws
ServletException {
System.out.println(
"init yes"
);
}
@Override
public
void
doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws
IOException, ServletException {
System.out.println(
"filter test"
);
filterChain.doFilter(servletRequest, servletResponse);
// 傳遞給下一個Filter進行處理
return
;
}
@Override
public
void
destroy() {
System.out.println(
"destroy yes"
);
}
}
|
然后在配置中使該Filter生效:
<filter>
<filter-name>filtertest</filter-name>
<filter-
class
>FilterTest</filter-
class
>
</filter>
<filter-mapping>
<filter-name>filtertest</filter-name>
<url-pattern>/point/*</url-pattern>
</filter-mapping>
|
這樣,當我們訪問/point/*類型的url,控制台輸出如下:
init yes
filter test
controller
三.比較
同時配置過濾器和攔截器然后請求,結果如下:
init yes
filter test
interceptortest pre
controller
interceptortest post
interceptortest after
可以看到filter優先於interceptor被調用。
過濾器和攔截器主要區別如下:
1.二者適用范圍不同。Filter是Servlet規范規定的,只能用於Web程序中,而攔截器既可以用於Web程序,也可以用於Application、Swing程序中。
2.規范不同。Filter是在Servlet規范定義的,是Servlet容器支持的,而攔截器是在Spring容器內的,是Spring框架支持的。
3.使用的資源不同。同其他代碼塊一樣,攔截器也是一個Spring的組件,歸Spring管理,配置在Spring文件中,因此能使用Spring里的任何資源、對象(各種bean),而Filter不行。
4.深度不同。Filter只在Servlet前后起作用,而攔截器能夠深入到方法前后、異常跑出前后等,攔截器的使用有更大的彈性。