1. 程式人生 > >springMVC學習-處理器攔截器

springMVC學習-處理器攔截器

處理器攔截器
SpringMVC的處理器攔截器類似於Servlet 開發中的過濾器Filter,用於對處理器進行預處理和後處理。
1)常見應用場景
1、日誌記錄
2、許可權檢查
3、效能監控
4、通用行為 例如讀取使用者cookie
5、OpenSessionInView 例如在Hibernate中,在進入處理器前開啟Session,在完成後關閉Session。

2)攔截器介面
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;


void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;


void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
}


preHandle方法 
預處理回撥方法,實現處理器的預處理,第三個引數為的處理器(本次請求要訪問的那個Controller)
返回值:true表示繼續流程(如呼叫下一個攔截器或處理器)
false表示流程中斷(如登入檢查失敗),不會繼續呼叫其他的攔截器或處理器,此時我們需要通過response來產生響應

postHandle方法
後處理回撥方法,實現處理器的後處理(但在渲染檢視之前),此時我們可以通過modelAndView對模型資料進行處理或對檢視進行處理,modelAndView也可能為null。

afterCompletion方法
整個請求處理完畢回撥方法,即在檢視渲染完畢時回撥

3)攔截器介面卡
有時候我們可能只需要實現三個回撥方法中的某一個,如果實現HandlerInterceptor 介面的話,三個方法必須實現,不管你需不需要,此時spring 提供了一個HandlerInterceptorAdapter 介面卡(介面卡模式),允許我們只實現需要的回撥方法。
在HandlerInterceptorAdapter中,對HandlerInterceptor 介面中的三個方法都進行了空實現,其中preHandle方法的返回值,預設是true

4)測試一個攔截器
攔截器程式碼:
public class MyInterceptor1 extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("MyInterceptor1 preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1 postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor1 afterCompletion");
}
}


配置檔案:
<bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>


<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
</list>
</property>
</bean>


訪問一個測試的Controller檢視結果:
MyInterceptor1 preHandle
TestController執行
MyInterceptor1 postHandle
MyInterceptor1 afterCompletion


5)測試倆個攔截器
倆個攔截器的程式碼和上面類似,只是每個輸出的內容不同
配置檔案:
<bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean name="handlerInterceptor2" class="com.briup.web.interceptor.MyInterceptor1"/>


<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>


訪問一個測試的Controller檢視結果:
MyInterceptor1 preHandle
MyInterceptor2 preHandle
TestController執行
MyInterceptor2 postHandle
MyInterceptor1 postHandle
MyInterceptor2 afterCompletion
MyInterceptor1 afterCompletion

注意:<list>標籤中引用攔截器的順序會影響結果輸出的順序



6)如果Controller等採用的註解配置,那麼攔截器需要mvc標籤進行配置
注意:每個<mvc:interceptor>只能配置一個攔截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="handlerInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>


例如1: 注意/*和/**的區別
<mvc:interceptors>
<!-- 下面所有的mvc對映路徑都會被這個攔截器攔截 -->
<bean class="com.briup.web.interceptor.MyInterceptor1" />


<mvc:interceptor>
<mapping path="/**"/>
<exclude-mapping path="/admin/**"/>
<bean class="com.briup.web.interceptor.MyInterceptor2" />
</mvc:interceptor>
<mvc:interceptor>
<mapping path="/secure/*"/>
<bean class="com.briup.web.interceptor.MyInterceptor3" />
</mvc:interceptor>
</mvc:interceptors>



7)攔截器是單例
因此不管多少使用者請求多少次都只有一個攔截器實現,即執行緒不安全。
所以在必要時可以在攔截器中使用ThreadLocal,它是和執行緒繫結,一個執行緒一個ThreadLocal,A 執行緒的ThreadLocal只能看到A執行緒的ThreadLocal,不能看到B執行緒的ThreadLocal。

8)記錄執行Controller所用時間
public class TimeInterceptor extends HandlerInterceptorAdapter{
//攔截器是單例,不是執行緒安全的,所以這裡使用ThreadLocal
private ThreadLocal<Long> local = new ThreadLocal<>();

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
long start = System.currentTimeMillis();
local.set(start);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
long end = System.currentTimeMillis();
System.out.println("共耗時:"+(end-local.get()));
}
}




9)登入檢查
public class LoginInterceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//請求到登入頁面放行
if(request.getServletPath().startsWith("/login")) {
return true;
}


//如果使用者已經登入放行
if(request.getSession().getAttribute("username") != null) {
return true;
}


//重定向到登入頁面
response.sendRedirect(request.getContextPath() + "/login");


return false;
}
}



注意:推薦能使用servlet規範中的過濾器Filter實現的功能就用Filter實現,因為HandlerInteceptor只有在SpringWebMVC環境下才能使用,因此Filter是最通用的、最先應該使用的。