SpringMVC 5:處理器攔截器
※. 處理器攔截器
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是最通用的、最先應該使用的。
1.控制器
package com.briup.web.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class FiveController
implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv=
new ModelAndView();
System.out.println("FiveController.....");
mv.setViewName("hello");
return mv;
}
}
2.構建攔截器
package com.briup.web.Interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class defineInterceptor
extends HandlerInterceptorAdapter{
/*執行處理器之前的操作,
* 相當於前置通知
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("before....defineInterceptor1");
//true就是放行,原來執行那個
//controller接著執行那個controller
//false終止原來的請求
return true;
}
/*
* Controller控制器執行之後,
* 檢視渲染之前做的操作
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle....defineInterceptor1");
}
/*
* 在檢視解析器渲染檢視之後的操作
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion....defineInterceptor1");
}
}
3.spring.xml檔案配置
<!-- 配置對映器 ,指明類url和處理請求
類的物件對應規則
http://localhost:8888/jd1812_MVC/hello.do
<bean name="/hello.do" class="com.briup.web.controller.FirstController">
-->
<bean name="myInterceptor"
class="com.briup.web.Interceptor.defineInterceptor"></bean>
<!-- 一旦自定義了攔截器,
對映器就不能省略 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<!-- 配置攔截器
,在對映器中配置的攔截器,對
所有的controller處理器生效
interceptors List<Object>
-->
<property name="interceptors">
<array>
<ref bean="myInterceptor"/>
</array>
</property>
</bean>
<!-- 介面卡,預設,控制器實現了介面controller
裡面有重寫方法的時候配置SimpleControllerHandlerAdapter -->
<bean class="com.briup.web.adapter.DefineAdapter"></bean>
<bean name="/five"
class="com.briup.web.controller.FiveController"></bean>