1、概述
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
2、简单示例
2.1.继承 HandlerInterceptorAdapter 抽象类实现一个拦截器。代码如下:
public class DemoInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("[DemoInterceptor]preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("[DemoInterceptor]postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("[DemoInterceptor]afterCompletion");
}
}
2.2.在指定给 DispatcherServlet 的配置文件中新增相关拦截器配置。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.ryan.springtest.interceptor.DemoInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
此时启动服务访问任一 URL,即可看到相应的输出信息。
[DemoInterceptor]preHandle
[DemoInterceptor]postHandle
[DemoInterceptor]afterCompletion
3、示例分析
简单分析一下上面的实例代码。
通过观察 HandlerInterceptorAdapter 的继承关系与数据结构,可知 HandlerInterceptorAdapter 实现了 AsyncHandlerInterceptor 与 HandlerInterceptor 接口,并对里面的抽象方法做了默认实现。
- preHandle:Controller 执行之前执行,返回 true 表示继续流程,false 中断当前流程,但会执行之前拦截器的 afterCompletion 方法;
- postHandle:Controller 方法调用之后,视图渲染之前执行;
- afterCompletion:渲染视图之后执行;
- afterConcurrentHandlingStarted:涉及到 Spring 的异步处理特性,先不讨论。
3.1、运行流程图如下:
4、源码分析
源码的分析将分为三部分:拦截器的加载,拦截器链的生成,拦截器链的执行。
4.1、拦截器的加载
配置文件简析
观察示例代码中的配置文件,省略了与分析无关的配置后,如下所示:
<beans
xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.ryan.springtest.interceptor.DemoInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>
配置信息分为两部分:(1)声明 Schema 的命名空间并指定 xsd 文件;(2)拦截器的具体配置。
interceptors 的配置采用了 Schema Based XML 的组件定义方式,这是 Spring 为了简化组件配置而引入的重要手段,这里不作展开。
通过配置文件,我们可以定位到具体解析拦截器配置的类,类的路径在 spring-webmvc 下的 META-INF 中的 spring.handlers 文件中指定。
spring.handlers 文件内容如下,可知实际解析配置的类是 MvcNamespaceHandler。
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
暂时不对 MvcNamespaceHandler 进行具体的分析,先来看看 MvcNamespaceHandler 是在何时被创建和执行的。
4.2、拦截器的加载过程
拦截器的加载是在 DispatcherServlet 初始化的过程中完成的,下面通过 DispatcherServlet 初始化时的调用图来了解一下拦截器加载的具体触发时机。
通过继承关系,可以发现 DispatcherServlet 实际上是一个 Servlet,根据 Servlet 的规范,服务启动时会调用 Servlet 的 init 方法,init 方法在 HttpServletBean 中实现,HttpServletBean 又会调用 FrameworkServlet 来创建 SpringMVC 单独的容器,并向容器进行了设置,然后调用容器的 refresh 方法触发容器的初始化操作。
在 DispatcherServlet 的初始化中,分为两个层次。
- DispatcherServlet 对容器的初始化。
- 容器对配置文件的解析以及相关组件的初始化。
所以真正初始化拦截器的动作是由容器(XmlWebApplicationContext)来完成的,观察下图容器初始化过程。
上图展示的是,从 XmlWebApplicationContext 的 refresh 方法开始,到调用 MvcNamespaceHandler 解析拦截器配置的执行过程。
初始化过程涉及到多个组件间的调用,简要分析如下。
- XmlWebApplicationContext 中创建了 beanFactory,通过 beanFactory 获取到 XmlBeanDefinitionReader 实例,接着获取到配置文件的路径,使用 XmlBeanDefinitionReader 来解析配置文件。
- XmlBeanDefinitionReader 将配置文件转换为 Document 对象,并创建了 BeanDefinitionDocumentReader 对象来解析 Document 对象。
- BeanDefinitionDocumentReader 遍历 Document 中的所有节点,拦截器的配置节点不属于默认的配置节点,将创建对应的 NamespaceHandler 来解析,即上文提到的 MvcNamespaceHandler。
- 创建 MvcNamespaceHandler 后,首先会调用对应的 init 方法注册所有的 BeanDefinitionParser,接着会遍历注册的 BeanDefinitionParser,找到合适的 BeanDefinitionParser 对传入的节点进行解析。
下面开始对 MvcNamespaceHandler 的分析,观察 MvcNamespaceHandler 的继承关系。
MvcNamespaceHandler 继承了 NamespaceHandlerSupport 抽象类并实现了 NamespaceHandler 接口,上文的分析提到,创建 MvcNamespaceHandler 后首先会调用 init 方法进行初始化,init 方法在 NamespaceHandler 中定义并在 MvcNamespaceHandler 中实现,截取代码如下。
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
//略
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
//略
}
}
init 方法中,调用了 registerBeanDefinitionParser 方法注册了相关的解析器,拦截器对应的解析器是 InterceptorsBeanDefinitionParser。
private final Map<String, BeanDefinitionParser> parsers = new HashMap<String, BeanDefinitionParser>();
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
registerBeanDefinitionParser 方法在 NamespaceHandlerSupport 中实现,如上所示,向 parsers 这个 Map 中 put 进了对应的 Key 与 Value。
初始化完成之后,便会开始遍历配置文件中的节点,当扫描到 <mvc:interceptors> 节点时,便会调用 MvcNamespaceHandler 中的 parse 方法进行解析。
parse 方法在 NamespaceHandler 中定义,并在 NamespaceHandlerSupport 中实现。
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
如上所示,获取到节点的 localName,即 interceptors,接着从 parsers 获取对应的解析器,调用解析器的 parse 方法进行解析,即上文注册的 InterceptorsBeanDefinitionParser,具体代码如下所示。
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compDefinition = new
CompositeComponentDefinition(element.getTagName(),
parserContext.extractSource(element));
parserContext.pushContainingComponent(compDefinition);
RuntimeBeanReference pathMatcherRef = null;
if (element.hasAttribute("path-matcher")) {
pathMatcherRef = new RuntimeBeanReference(element.getAttribute("path-matcher"));
}
List<Element> interceptors = DomUtils.getChildElementsByTagName(element, "bean", "ref", "interceptor");
for (Element interceptor : interceptors) {
RootBeanDefinition mappedInterceptorDef = new
RootBeanDefinition(MappedInterceptor.class);
mappedInterceptorDef.setSource(parserContext.extractSource(interceptor));
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ManagedList<String> includePatterns = null;
ManagedList<String> excludePatterns = null;
Object interceptorBean;
if ("interceptor".equals(interceptor.getLocalName())) {
includePatterns = getIncludePatterns(interceptor, "mapping");
excludePatterns = getIncludePatterns(interceptor, "exclude-mapping");
Element beanElem = DomUtils.getChildElementsByTagName(interceptor, "bean", "ref").get(0);
interceptorBean = parserContext.getDelegate().parsePropertySubElement(beanElem, null);
}
else {
interceptorBean = parserContext.getDelegate().parsePropertySubElement(interceptor, null);
}
mappedInterceptorDef.getConstructorArgumentValues().
addIndexedArgumentValue(0, includePatterns);
mappedInterceptorDef.getConstructorArgumentValues().
addIndexedArgumentValue(1, excludePatterns);
mappedInterceptorDef.getConstructorArgumentValues().
addIndexedArgumentValue(2, interceptorBean);
if (pathMatcherRef != null) {
mappedInterceptorDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
}
String beanName = parserContext.getReaderContext().registerWithGeneratedName(mappedInterceptorDef);
parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, beanName));
}
parserContext.popAndRegisterContainingComponent();
return null;
}
传入的 element 即为 mvc:interceptors 节点对象,上述代码主要做了如下一些事情:
- 获取 mvc:interceptors 节点下的 bean、ref、interceptor 节点数组。
- 遍历节点数组,创建 MappedInterceptor 对象。若节点对象为 interceptor,将mapping、exclude-mapping、bean 或 ref 节点信息设置到 MappedInterceptor中。若节点对象为 bean 或 ref,则仅设置 bean 或 ref 节点信息,mapping 与 exclude-mapping 则设置为空,即会拦截所有请求。
- 将 MappedInterceptor 对象注册到容器中,以便后续使用。
至此,我们已经根据配置文件中的拦截器配置,生成了 MappedInterceptor 拦截器对象。
SpringMVC 接收到 Web 请求时,会由 HandlerMapping 生成对应的拦截器链,为了便于处理,SpringMVC 还会将 MappedInterceptor 对象加载到 HandlerMapping 的成员变量中,这一步的加载稍微隐藏得比较深,可以观察下面的流程图。
handlerMaping初始化
FrameworkServlet 在 configureAndRefreshWebApplicationContext 方法中,向容器中注册了一个监听器(ContextRefreshListener),正是这一步操作触发了上述流程,简要分析如下:
- AbstractApplicationContext 在 refresh 方法中加载完拦截器后,会调用 finishRefresh 方法,触发 ContextRefreshListener 的执行。
- ContextRefreshListener 触发 FrameworkServlet 的 onApplicationEvent 方法,把初始化的控制权交给 DispatcherServlet。
- DispatcherServlet 触发 HandlerMapping 进行初始化,HandlerMapping 默认实现之一是 RequestMappingHandlerMapping,这里以此为例进行说明。
- 观察 RequestMappingHandlerMapping 的继承关系,实现了 ApplicationContextAware 接口,Spring 在初始化 ApplicationContextAware 实现类的时候,会调用其 setApplicationContext 方法。
- setApplicationContext 方法由 ApplicationObjectSupport 实现,最终会调用 AbstractHandlerMapping 的 initApplicationContext 方法,完成拦截器的加载,相关代码展示如下。
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
detectMappedInterceptors 方法探测 ApplicationContext 中已经解析过的 MappedInterceptor,全部存放到 AbstractHandlerMapping 的 adaptedInterceptors 属性上,extendInterceptors 方法留给子类扩展,目前还没有子类实现。
至此,拦截器的相关信息已经加载完成,后续有请求进来的时候就可以直接进行匹配。
4.3、拦截器链生成
当请求进来的时候会被匹配的拦截器拦截,多个拦截器组成一个拦截器链,并按照我们定义的顺序执行。
SpringMVC 中的所有请求都需要经过 DispatcherServlet 进行分发,拦截器链也是在其中生成的,当请求被 DispatcherServlet 拦截后,会在 doDispatch 方法中进行分发,下面截取部分关键代码。
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);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
//略
}
catch (Exception ex) {
//略
}
}
catch (Exception ex) {
//略
}
}
每个请求需生成对应的 HandlerExecutionChain,HandlerExecutionChain 中包含了目标 service 和对应的拦截器链,从上面的源码可以看到,getHandler 会返回一个 HandlerExecutionChain。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name ‘" + getServletName() + "‘");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
getHandler 方法中会遍历找到所有 HandlerMapping,调用其中的 getHandler 方法,直到找到一个合适的。其中就包括上文提到的 RequestMappingHandlerMapping。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
//略
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//略
return executionChain;
}
getHandler 的实现在 AbstractHandlerMapping 类中,找到对应请求的 handler 后,便会调用 getHandlerExecutionChain 方法获取 HandlerExecutionChain,这里就是生成拦截器链的关键代码。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
getHandlerExecutionChain 方法中,遍历 AbstarctHandlerMapping 的 adaptedInterceptors 属性,使用默认的 pathMatcher,判断当前的请求是否符合拦截条件,若符合则将 mappedInterceptor 放进 HandlerExecutionChain 中。
至此一个 HandlerExecutionChain 便构建好了,包含一个 handler 和我们想要的拦截器链。
4.4、拦截器链执行
获取到拦截器链之后,接下来就是按照一定的顺序执行其中的方法,回到上一步分析开始的 doDispatch 方法中,可以看到拦截器链的具体执行过程。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
//略
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//略
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//略
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//略
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
//略
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
//略
}
}
在本文开始的代码示例中,我们实现了拦截器中的三个方法,这三个方法的具体执行时机和顺序分析如下。
(1)preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
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;
}
preHandle 的执行顺序在 HandlerExecutionChain 中控制,正序遍历拦截器链,执行拦截器的 preHandle 方法,并会记录下当前执行的下标,若 preHandle 返回 false 则会执行 triggerAfterCompletion 方法,若异常则抛出。
(2)postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
ha.handle 执行目标 Controller 的方法,执行完后就会执行 applyPostHandle。
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable 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);
}
}
}
postHandle 的执行顺序在 HandlerExecutionChain 中控制,倒序遍历拦截器链,并执行拦截器的 postHandle 方法,若异常则抛出。
(3)afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
在末尾的 processDispatchResult 方法和异常处理的 triggerAfterCompletion 方法中,都会调用 HandlerExecutionChain的triggerAfterCompletion 方法。
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable 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);
}
}
}
}
triggerAfterCompletion 方法从指定下标 interceptorIndex 开始,倒序遍历拦截器链,并执行拦截器的 afterCompletion 方法,若异常则记录到日志中,interceptorIndex 的下标是在 preHandle 中设置的,这里就是上文中提到的当 preHandle 返回 false,仍会执行之前拦截器的 afterCompletion 方法的原因。
至此,拦截器链的执行便完成了。
原文地址:https://www.cnblogs.com/Pibaosi/p/10459769.html