1. 程式人生 > >SpringMVC 5:處理器攔截器

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>