标签:概述 value gate event 分发 容器 调用 解析 成员
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 接口,并对里面的抽象方法做了默认实现。
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 的初始化中,分为两个层次。
所以真正初始化拦截器的动作是由容器(XmlWebApplicationContext)来完成的,观察下图容器初始化过程。
上图展示的是,从 XmlWebApplicationContext 的 refresh 方法开始,到调用 MvcNamespaceHandler 解析拦截器配置的执行过程。
初始化过程涉及到多个组件间的调用,简要分析如下。
下面开始对 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 节点对象,上述代码主要做了如下一些事情:
至此,我们已经根据配置文件中的拦截器配置,生成了 MappedInterceptor 拦截器对象。
SpringMVC 接收到 Web 请求时,会由 HandlerMapping 生成对应的拦截器链,为了便于处理,SpringMVC 还会将 MappedInterceptor 对象加载到 HandlerMapping 的成员变量中,这一步的加载稍微隐藏得比较深,可以观察下面的流程图。
handlerMaping初始化
FrameworkServlet 在 configureAndRefreshWebApplicationContext 方法中,向容器中注册了一个监听器(ContextRefreshListener),正是这一步操作触发了上述流程,简要分析如下:
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 方法的原因。
至此,拦截器链的执行便完成了。
0002 - Spring MVC 拦截器源码简析:拦截器加载与执行
标签:概述 value gate event 分发 容器 调用 解析 成员
原文地址:https://www.cnblogs.com/Pibaosi/p/10459769.html