博客索引web
HandlerInterceptor
至於爲何要繼承這個類,下面講解原理的時候會提到。 咱們寫一個簡單的HelloInterceptor
攔截器,輸出hellospring
public class HelloInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("hello,i am HelloInterceptor");
return true;
}
}
複製代碼
WebConfiguration
來繼承WebMvcConfigurer
,以下:@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Bean
HelloInterceptor interceptor() {
return new HelloInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//註冊攔截器HelloInterceptor,攔截全部請求,除了/test
registry.addInterceptor(interceptor()).addPathPatterns("/**").excludePathPatterns("/test");
}
}
複製代碼
@RestController
public class MvcController {
@GetMapping("/test")
private String test() {
System.out.println("i am test ......");
return "test";
}
@GetMapping("/test1")
private String test1() {
System.out.println("i am test1 ......");
return "test1";
}
}
複製代碼
訪問localhost:8080/test 輸出i am test ...... 訪問localhost:8080/test1 輸出 i am HelloInterceptor i am test ......數組
直接看配置類中的方法。bash
@Override
public void addInterceptors(InterceptorRegistry registry) {
//註冊攔截器HelloInterceptor,攔截全部請求,除了/test
registry.addInterceptor(interceptor()).addPathPatterns("/**").excludePathPatterns("/test");
}
複製代碼
點進去發現。InterceptorRegistry
裏面有個registrations對象是一個InterceptorRegistration
類型的攔截器列表,addInterceptor(HandlerInterceptor)方法將攔截器包裝成InterceptorRegistration
對象並添加到registrations對象。而後還會發現裏面有個getInterceptors()方法返回全部的攔截器。咱們用idea搜索一下,看那些地方調用這個方法。mvc
public class InterceptorRegistry {
private final List<InterceptorRegistration> registrations = new ArrayList<>();
public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
InterceptorRegistration registration = new InterceptorRegistration(interceptor);
this.registrations.add(registration);
return registration;
}
public InterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor) {
WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor);
InterceptorRegistration registration = new InterceptorRegistration(adapted);
this.registrations.add(registration);
return registration;
}
/**
* 返回全部註冊的攔截器
*/
protected List<Object> getInterceptors() {
return this.registrations.stream()
.sorted(INTERCEPTOR_ORDER_COMPARATOR)
// 這裏很關鍵,類型是MappedInterceptor
.map(InterceptorRegistration::getInterceptor)
.collect(Collectors.toList());
}
private static final Comparator<Object> INTERCEPTOR_ORDER_COMPARATOR =
OrderComparator.INSTANCE.withSourceProvider(object -> {
if (object instanceof InterceptorRegistration) {
return (Ordered) ((InterceptorRegistration) object)::getOrder;
}
return null;
});
}
複製代碼
org.springframework.web.servlet.config.annotation.InterceptorRegistration#getInterceptor()
: 返回MappedInterceptor類型的攔截器,返回值yongObject接收,也就是說咱們自定義的攔截器會被包裝成MappedInterceptor
類型,而MappedInterceptor
又繼承了HandlerInterceptor
,還提了匹配URL的功能,便於各類自定義開發。app
protected Object getInterceptor() {
if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) {
return this.interceptor;
}
// 攔截路徑的數組,好比咱們自定義的HelloInterceptor,那麼這裏的include=["/**"]
String[] include = StringUtils.toStringArray(this.includePatterns);
// 排除路徑的數組,這裏的exclude=["/test"]
String[] exclude = StringUtils.toStringArray(this.excludePatterns);
MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor);
if (this.pathMatcher != null) {
mappedInterceptor.setPathMatcher(this.pathMatcher);
}
return mappedInterceptor;
}
複製代碼
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getInterceptors()
調用了以前的
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
// 獲取全部註冊過的攔截器
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
複製代碼
繼續搜索,看哪一個地方調用這個getInterceptors()方法。cors
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping
: 建立
RequestMappingHandlerMapping
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
// 給RequestMappingHandlerMapping設置攔截器
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
···省略其餘
return mapping;
}
複製代碼
org.springframework.web.servlet.handler.AbstractHandlerMapping#setInterceptors(Object... interceptors)
:抽象類,封裝了RequestMappingHandlerMapping
大部分方法,能夠理解成是一種模板模式,其中幾個重要的方法。async
HandlerInterceptor
類型或者WebRequestInterceptor
,要否則就會拋出異常。這就解答了咱們自定義異常爲何要繼承HandlerInterceptor
。public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered, BeanNameAware {
@Nullable
private Object defaultHandler;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
private final List<Object> interceptors = new ArrayList<>();
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
public void setInterceptors(Object... interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors));
}
// 適配攔截器
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
if (interceptor instanceof HandlerInterceptor) {
return (HandlerInterceptor) interceptor;
}
else if (interceptor instanceof WebRequestInterceptor) {
return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
}
else {
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
}
}
// 初始化攔截器
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
複製代碼
因此看到這裏,就能夠知道咱們自動定義的攔截器最後被添加到了AbstractHandlerMapping
中。分析到這裏差很少快結束了。ide
HandlerExecutionChain
在最後分析以前,要先了解一下這個類HandlerExecutionChain
攔截器鏈,這是由handle與一系列的攔截器組成的,也就是咱們自定義的攔截器會被放入這個類中,進行執行,話很少說,直接debug。post
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;
}
複製代碼
在org.springframework.web.servlet.DispatcherServlet#doDispatch
中打上斷點,請求http://localhost:8080/test,
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 獲取HandlerExecutionChain,核心地方,其餘地方先略過
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 執行攔截器中的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// handle真正執行
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
// 執行攔截器中的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
複製代碼
咱們發現返回HandlerExecutionChain
的方法getHandler(processedRequest)
,咱們知道HandlerMapping
接口中只有一個方法返回HandlerExecutionChain
,而它的實現類剛好是咱們上面分析的AbstractHandlerMapping
。
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
複製代碼
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 獲取handle,有興趣的能夠深刻了解這裏
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 本文的關注點,獲取HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 獲取請求路徑,好比咱們的URL是localhost:8080/test 這裏獲得的lookupPath就是/test
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
// 遍歷攔截器,判斷是否是MappedInterceptor,若是是的話,則判斷路徑是否知足自定義的路徑
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
// MappedInterceptor也就是咱們自定義的攔截器,而後將路徑/test與MappedInterceptor裏面的excludePatterns和includePatterns進行匹配
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
複製代碼
org.springframework.web.servlet.handler.MappedInterceptor#matches(String ,PathMatcher)
:
public final class MappedInterceptor implements HandlerInterceptor {
@Nullable
private final String[] includePatterns;
@Nullable
private final String[] excludePatterns;
private final HandlerInterceptor interceptor;
@Nullable
private PathMatcher pathMatcher;
// 由於excludePatterns數組裏麪包含"/test",因此不匹配,返回false,這個請求中的攔截器連中就沒有咱們自定義的HelloInterceptor
public boolean matches(String lookupPath, PathMatcher pathMatcher) {
PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher);
if (!ObjectUtils.isEmpty(this.excludePatterns)) {
for (String pattern : this.excludePatterns) {
if (pathMatcherToUse.match(pattern, lookupPath)) {
return false;
}
}
}
if (ObjectUtils.isEmpty(this.includePatterns)) {
return true;
}
for (String pattern : this.includePatterns) {
if (pathMatcherToUse.match(pattern, lookupPath)) {
return true;
}
}
return false;
}
}
複製代碼
到此攔截器的原理就介紹完了,若是文章有錯誤或者你有什麼疑問,請留言或者經過郵箱聯繫我creazycoder@sina.com