SpringMVC springMVC的攔截器

1、 攔截器的做用

SpringMVC的處理器攔截器相似於Servlet開發中的過濾器Filter,用於對處理器進行預處理和後處理。開發者能夠本身定義一些攔截器來實現特定的功能。css

1.【攔截器鏈】(Interceptor Chain): 將攔截器按必定的順序鏈接成一條鏈。在訪問被攔截的方法或者字段時,攔截器鏈中的攔截器就會按照以前定義的順序被調用。html

2.過濾器與攔截器的區別java

攔截器是AOP思想的具體應用。web

過濾器 攔截器
servlet規範中的一部分,任何java web工程均可以使用 攔截器是SpringMVC框架本身的,只有使用了SpringMVC框架的工程才能使用
在url-pattern中配置了/*以後,能夠對全部要訪問的資源進行攔截 攔截器只會攔截訪問的控制器方法, 若是訪問的是jsp/html/css/image/js是不會進行攔截的

2、自定義攔截器的步驟

想要自定義攔截器,必須實現 HandlerInterceptor 接口。redis

1. 編寫一個普通類實現HandlerInterceptor接口

/** 自定義攔截器 */
public class HanderInterceptorOne implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("preHandle攔截器攔截了");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) {
        System.out.println("postHandle方法執行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) {
        System.out.println("afterCompletion方法執行了");
    }
}

2. 配置攔截器

配置springMVC.xmlspring

<!-- 配置攔截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="handlerInterceptorOne"
              class="com.itheima.myInterceptor.HanderInterceptorOne"/>
    </mvc:interceptor>
</mvc:interceptors>

3. 【*】測試運行結果【下面全部測試的運行的代碼都使用這一套】

1. 訪問的index.jspsession

<%-- 測試攔截器 --%>
<a href="${pageContext.request.contextPath}/interceptor/function">攔截器測試</a>

2. 控制器代碼mvc

/** 測試攔截器的控制器 */
@Controller
@RequestMapping("/interceptor")
public class InterceptorController {

    @RequestMapping("/function")
    public String testFunction() {
        System.out.println("控制器中的方法執行了");
        return "success";
    }
}

3. 運行結果app

preHandle攔截器攔截了
控制器中的方法執行了
postHandle方法執行了
afterCompletion方法執行了

3、攔截器的細節

1. 攔截器的放行框架

放行的含義是指,若是有下一個攔截器就執行下一個,若是該攔截器處於攔截器鏈的最後一個,則執行控制器中的方法。

攔截器放行.png

2. 攔截器中方法的說明

攔截器HandlerInterceptor接口中的三個方法詳解。

2.1 boolean preHandle 方法

2.1.1 方法源碼

default boolean preHandle(HttpServletRequest request, 
				HttpServletResponse response, 
				Object handler) throws Exception {
    return true;
}

2.1.2 如何調用:按照攔截器定義順序調用

2.1.3 什麼時候調用:只要配置了都會被調用

2.1.4 邏輯功能:若是開發者決定該攔截器對請求攔截處理後還要調用其餘的攔截器,或者是業務處理器去進行處理,則返回true。若是開發者決定不須要再調用其餘的組件去處理請求,則返回false。

2.2 void postHandle 方法

2.2.1 方法源碼

default void postHandle(HttpServletRequest request, 
				HttpServletResponse response, 
				Object handler, 
				@Nullable ModelAndView modelAndView) throws Exception {}

2.2.2 如何調用:按照攔截器定義逆序調用

2.2.3 什麼時候調用:攔截器鏈內全部攔截器返回成功時調用

2.2.4 邏輯功能:在業務處理器處理完請求後,可是DispatcherServlet向客戶端返回響應前被調用,在該方法中對用戶請求request進行處理。

2.3 void afterCompletion 方法

2.3.1 方法源碼

default void afterCompletion(HttpServletRequest request, 
				HttpServletResponse response, 
				Object handler, 
				@Nullable Exception ex) throws Exception {}

2.3.2 如何調用:按攔截器定義逆序調用

2.3.3 什麼時候調用:只有對應的preHandle返回true纔會被調用

2.3.4 邏輯功能在DispatcherServlet徹底處理完請求後被調用,能夠在該方法中進行一些資源清理的操做。

3. 攔截器的做用路徑

做用路徑能夠經過在配置文件中配置。能夠配置多個

<!-- 配置攔截器的做用範圍 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        -- 用於指定攔截的url,能夠配置多個

        <mvc:exclude-mapping path="/xxx"/>
        -- 用於指定排除的url,能夠配置多個

        <bean id="handlerInterceptorOne"
              class="com.itheima.myInterceptor.HanderInterceptorOne"/>
    </mvc:interceptor>
</mvc:interceptors>

4. 多個攔截器的執行順序

多個攔截器是按照配置的順序決定的,下面以前後配置HandlerInterceptorDemo1/HandlerInterceptorDemo2爲例:

4、正常流程測試案例

1. 攔截器1代碼

public class HandlerInterceptorDemo1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("攔截器1:preHandle攔截器攔截了");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("攔截器1:postHandle方法在DispatcherServlet響應前執行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("攔截器1:afterCompletion方法在DispatcherServlet響應後執行了");
    }
}

2. 攔截器2代碼

將攔截器中的輸出語句中的攔截器1換成攔截器2,類名換成HandlerInterceptorDemo2

3. 配置文件

<!-- 正常執行的攔截器配置,按照配置的順序執行 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="handlerInterceptorDemo1" class="com.itheima.myInterceptor.HandlerInterceptorDemo1"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="handlerInterceptorDemo2" class="com.itheima.myInterceptor.HandlerInterceptorDemo2"/>
    </mvc:interceptor>
</mvc:interceptors>

4. 測試代碼

訪問的index.jsp代碼和控制器代碼同上【*】測試運行結果...

5. 執行結果

攔截器1:preHandle攔截器攔截了
攔截器2:preHandle攔截器攔截了
控制器中的方法執行了
攔截器2:postHandle方法在DispatcherServlet響應前執行了
攔截器1:postHandle方法在DispatcherServlet響應前執行了
攔截器2:afterCompletion方法在DispatcherServlet響應後執行了
攔截器1:afterCompletion方法在DispatcherServlet響應後執行了

5、中斷流程測試

1. 攔截器1代碼

同上一個案例的攔截器1的代碼

2. 攔截器2代碼

public class HandlerInterceptorDemo2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("攔截器2:preHandle攔截器攔截了");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("攔截器2:postHandle方法在DispatcherServlet響應前執行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("攔截器2:afterCompletion方法在DispatcherServlet響應後執行了");
    }
}

3. 配置文件

同上一個案例的配置文件

4. 測試代碼

訪問的index.jsp代碼和控制器代碼同上【*】測試運行結果...

5. 執行結果

攔截器1:preHandle攔截器攔截了
攔截器2:preHandle攔截器攔截了
攔截器1:afterCompletion方法在DispatcherServlet響應後執行了

6、攔截器的簡單案例

驗證用戶是否登陸(認證用戶)

1. 實現思路

1.1 有一個登錄頁面,須要寫一個controller訪問頁面。

1.2 登錄頁面有一提交表單的動做。須要在controller中處理。

1.2.1 判斷用戶名密碼是否正確。
1.2.2 若是正確,向session(redis)中寫入用戶信息。
1.2.3 返回登錄成功。

1.3 攔截用戶請求,判斷用戶是否登錄。

1.3.1 若是用戶已經登錄。放行 1.3.2 若是用戶未登錄,跳轉到登錄頁面

2. 控制器代碼

@Controller
@RequestMapping("/user")
public class UserController {

    /** 跳轉到登錄頁面 */
    @RequestMapping("/jumplogin")
    public String jumpLogin(Model model) throws Exception {
        return "login";
    }

    /** 登錄提交 */
    @RequestMapping("/login")
    public String login(HttpSession session, String userid, String pwd) throws Exception {
        // 向session記錄用戶身份信息
        session.setAttribute("user", userid);
        return "success";
    }

    /** 退出登錄 */
    @RequestMapping("logout")
    public String logout(HttpSession session) throws Exception {
        // session 過時
        session.invalidate();

        return "login";
    }
}

3. 攔截器代碼

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        // 若是是登錄頁面則放行
        System.out.println("uri: " + request.getRequestURI());
        if (request.getRequestURI().contains("login")) {
            return true;
        }

        HttpSession session = request.getSession();

        // 若是用戶已登錄也放行
        if(session.getAttribute("user") != null) {
            return true;
        }

        // 用戶沒有登錄跳轉到登錄頁面
        request.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(request, response);
        return false;
    }
}

4. springMVC.xml配置

<!-- 登錄案例的攔截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="loginInterceptor" class="com.itheima.myInterceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

5. 登錄頁面login.jsp

<form action="${pageContext.request.contextPath}/user/login">
    用戶名:<input type="text" name="userid"> <br>
    密碼: <input type="password" name="pwd"> <br>
    <input type="submit" value="提交">
</form>
相關文章
相關標籤/搜索