遨遊springmvc之HandlerInterceptor

1.前言

在實際開發項目中,每每會有一種場景:好比須要往一類業務中加入共同的邏輯處理。由此springmvc引進了攔截器的概念。攔截器是動態攔截Action調用的對象。它提供了一種機制可使開發者能夠定義在一個action執行的先後執行的代碼,也能夠在一個action執行前阻止其執行,同時也提供了一種能夠提取action中可重用部分的方式,達到無需修改每一個處理器實現的目的。這就是咱們這一章須要介紹的HandlerInterceptorhtml

 

2.原理

2.1 攔截器的引入

HandlerInterceptor實際上是在HandlerExecutionChain中引入的,在springmvc核心處理器DispatcherServlet的doDispatcher方法中請注意如下關於攔截器使用到的代碼java

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	...
        //預處理
	if (!mappedHandler.applyPreHandle(processedRequest, response)) {
		return;
	}

	// Actually invoke the handler.
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

	if (asyncManager.isConcurrentHandlingStarted()) {
		return;
	}

	applyDefaultViewName(processedRequest, mv);
        //攔截器邏輯處理
	mappedHandler.applyPostHandle(processedRequest, response, mv);
	...
        //處理視圖
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	...
        //視圖渲染徹底以後處理內容
	finally {
		if (asyncManager.isConcurrentHandlingStarted()) {
			// Instead of postHandle and afterCompletion
			if (mappedHandler != null) {
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
			}
		}
		else {
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}

HandlerExecutionChain中包含了全部執行攔截器方法的代碼,web

/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

/**
 * Handler execution chain, consisting of handler object and any handler interceptors.
 * Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
 *
 * @author Juergen Hoeller
 * @since 20.06.2003
 * @see HandlerInterceptor
 */
public class HandlerExecutionChain {

	private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

	private final Object handler;

	private HandlerInterceptor[] interceptors;

	private List<HandlerInterceptor> interceptorList;

	private int interceptorIndex = -1;


	/**
	 * Create a new HandlerExecutionChain.
	 * @param handler the handler object to execute
	 */
	public HandlerExecutionChain(Object handler) {
		this(handler, (HandlerInterceptor[]) null);
	}

	/**
	 * Create a new HandlerExecutionChain.
	 * @param handler the handler object to execute
	 * @param interceptors the array of interceptors to apply
	 * (in the given order) before the handler itself executes
	 */
	public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
		if (handler instanceof HandlerExecutionChain) {
			HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
			this.handler = originalChain.getHandler();
			this.interceptorList = new ArrayList<HandlerInterceptor>();
			CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
			CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
		}
		else {
			this.handler = handler;
			this.interceptors = interceptors;
		}
	}


	/**
	 * Return the handler object to execute.
	 * @return the handler object
	 */
	public Object getHandler() {
		return this.handler;
	}

	public void addInterceptor(HandlerInterceptor interceptor) {
		initInterceptorList().add(interceptor);
	}

	public void addInterceptors(HandlerInterceptor... interceptors) {
		if (!ObjectUtils.isEmpty(interceptors)) {
			initInterceptorList().addAll(Arrays.asList(interceptors));
		}
	}

	private List<HandlerInterceptor> initInterceptorList() {
		if (this.interceptorList == null) {
			this.interceptorList = new ArrayList<HandlerInterceptor>();
			if (this.interceptors != null) {
				// An interceptor array specified through the constructor
				this.interceptorList.addAll(Arrays.asList(this.interceptors));
			}
		}
		this.interceptors = null;
		return this.interceptorList;
	}

	/**
	 * Return the array of interceptors to apply (in the given order).
	 * @return the array of HandlerInterceptors instances (may be {@code null})
	 */
	public HandlerInterceptor[] getInterceptors() {
		if (this.interceptors == null && this.interceptorList != null) {
			this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
		}
		return this.interceptors;
	}


	/**
	 * Apply preHandle methods of registered interceptors.
	 * @return {@code true} if the execution chain should proceed with the
	 * next interceptor or the handler itself. Else, DispatcherServlet assumes
	 * that this interceptor has already dealt with the response itself.
	 */
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

	/**
	 * Apply postHandle methods of registered interceptors.
	 */
	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}

	/**
	 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
	 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
	 * has successfully completed and returned true.
	 */
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = this.interceptorIndex; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				try {
					interceptor.afterCompletion(request, response, this.handler, ex);
				}
				catch (Throwable ex2) {
					logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
				}
			}
		}
	}

	/**
	 * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
	 */
	void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = interceptors.length - 1; i >= 0; i--) {
				if (interceptors[i] instanceof AsyncHandlerInterceptor) {
					try {
						AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
						asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
					}
					catch (Throwable ex) {
						logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
					}
				}
			}
		}
	}


	/**
	 * Delegates to the handler's {@code toString()}.
	 */
	@Override
	public String toString() {
		if (this.handler == null) {
			return "HandlerExecutionChain with no handler";
		}
		StringBuilder sb = new StringBuilder();
		sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");
		if (!CollectionUtils.isEmpty(this.interceptorList)) {
			sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");
			if (this.interceptorList.size() > 1) {
				sb.append("s");
			}
		}
		return sb.toString();
	}

}

 

2.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是在請求處理以前會執行的方法,至關因而一種攔截器執行前的預處理。springmvc攔截器執行時聽從鏈式執行,也就是當前一個攔截器返回false時,則以後的攔截器也不將執行,代碼能夠在HandlerExecutionChain中的applyPreHandle體現。
  • postHandle是在請求處理完返回視圖,可是視圖沒有渲染以前執行的方法。postHandle的執行順序恰好和preHandle相反,preHandle按照的是先進先處理,postHandle則是按照先進後處理的順序。爲何這樣請看HandlerExecutionChain中applyPreHandle、applyPostHandle方法for循環的迭代規則
  • afterCompletion是先請求徹底處理好(視圖渲染)以後執行的。afterCompletion方法執行的順序和postHandle執行的順序是一致的。這個方法的主要做用是用於進行資源清理工做的。

2.3 執行方法圖解

 

3.配置

攔截器的配置加在mvc:interceptors節點中,以下spring

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/user/**"/>
        <mvc:mapping path="/bind/**"/>
        <mvc:exclude-mapping path="/test/**"/>
        <bean class="com.kings.template.mvc.interceptor.Customer0Interceptor"/>
    </mvc:interceptor>
    <bean class="com.kings.template.mvc.interceptor.Customer1Interceptor"/>
</mvc:interceptors>

mvc:interceptors下能夠添加多個mvc:interceptor或者bean,mvc:interceptor能夠設定攔截器攔截的路徑和不要攔截的路徑,而且提供對應的處理類,而若是在mvc:interceptors直接使用bean則會被所有攔截express

 

4.實例

4.1 Customer0Interceptor

package com.kings.template.mvc.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Customer0Interceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Customer0Interceptor 執行 preHandle預處理");
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Customer0Interceptor 執行 postHandle邏輯");
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("Customer0Interceptor 執行 afterCompletion最終邏輯");
    }
}

4.2 Customer1Interceptor

package com.kings.template.mvc.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Customer1Interceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Customer1Interceptor 執行 preHandle預處理");
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Customer1Interceptor 執行 postHandle邏輯");
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("Customer1Interceptor 執行 afterCompletion最終邏輯");
    }
}

4.3 配置

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/user/**"/>
        <mvc:mapping path="/bind/**"/>
        <mvc:exclude-mapping path="/test/**"/>
        <bean class="com.kings.template.mvc.interceptor.Customer0Interceptor"/>
    </mvc:interceptor>
    <bean class="com.kings.template.mvc.interceptor.Customer1Interceptor"/>
</mvc:interceptors>

4.4 調用

訪問http://localhost:8080/kingstemplate/user/1apache

打印信息以下mvc

Customer0Interceptor 執行 preHandle預處理
Customer1Interceptor 執行 preHandle預處理
[2016-10-22 14:27:11] [DEBUG] org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor(250) : Written [/Location] as "text/html" using [org.springframework.http.converter.StringHttpMessageConverter@2f412a47]
Customer1Interceptor 執行 postHandle邏輯
Customer0Interceptor 執行 postHandle邏輯
[2016-10-22 14:27:11] [DEBUG] org.springframework.web.servlet.DispatcherServlet(1044) : Null ModelAndView returned to DispatcherServlet with name 'springmvc': assuming HandlerAdapter completed request handling
Customer1Interceptor 執行 afterCompletion最終邏輯
Customer0Interceptor 執行 afterCompletion最終邏輯
[2016-10-22 14:27:11] [DEBUG] org.springframework.web.servlet.DispatcherServlet(1000) : Successfully completed request

 

5. 與過濾器Filter的區別

過濾器能夠簡單理解爲「取你所想取」,忽視掉那些你不想要的東西;攔截器能夠簡單理解爲「拒你所想拒」,關心你想要拒絕掉哪些東西,好比一個BBS論壇上攔截掉敏感詞彙。app

1.攔截器是基於java反射機制的,而過濾器是基於函數回調的。less

2.過濾器依賴於servlet容器,而攔截器不依賴於servlet容器。async

3.攔截器只對action起做用,而過濾器幾乎能夠對全部請求起做用。

4.攔截器能夠訪問action上下文、值棧裏的對象,而過濾器不能。

5.在action的生命週期裏,攔截器能夠多起調用,而過濾器只能在容器初始化時調用一次。

 

發現一個機智的導航😳

相關文章
相關標籤/搜索