# 自定义拦截器及原理

1. 用法

博客索引web

1.1 继承 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;
    }

}
复制代码

1.2 建立一个配置类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");
    }

}
复制代码

1.3 大功告成,测试。

@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 ......数组

2. 原理

直接看配置类中的方法。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

  1. initInterceptors(): 初始化拦截器,将interceptors对象里面的拦截器添加到adaptedInterceptors中,这里能够看出添加的拦截器必须是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

2.1 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

相关文章
相关标签/搜索