码迷,mamicode.com
首页 > 编程语言 > 详细

springmvc源码分析之拦截器

时间:2018-08-05 13:00:32      阅读:194      评论:0      收藏:0      [点我收藏+]

标签:代码   oca   tor   tpm   继承   解析过程   chain   nfa   extension   

 

前言

前面我们简单地分析了两个过程,本文将会来介绍请求过来是如何找到对应的Controller中的方法的。

概述

在分析之前,我们先记住几个类名,HandlerMapping接口,RequestMapingHandlerMapping类,HandlerMethod类。

  • HandlerMapping接口:请求和处理方法的映射接口,提供了getHandler方法,我们可以通过这个接口的实现类来完成请求和处理方法的映射
  • RequestMappingHandlerMapping:请求和处理方法映射关系的实现类,这是方法是我们分析的重点
  • HandlerMethod:处理方法,这个方法封装了许多东西,比如我们加了@controller注解的对象、对应Controller的方法、方法的参数、方法的注解、方法参数的注解等等。

技术分享图片

关注一下,RequestMappingHandlerMapping的继承关系,几点说明一下

  • 实现了HandlerMapping接口
  • 实现了InitialzingBean接口,这个接口我们知道,在IOC完成注入后,会有一个初始化处理过程,这个过程中会调用这个接口实现类的afterPropertiesSet方法,对于RequestMappingHandlerMapping来说,主要是在这个方法里完成了映射关系的注册
  • 实现了ApplicationAware接口,会在这个实现方法时进行拦截的器的查找的注册

技术分享图片

这里是HttpMethod的属性,本篇我们关注下bean和method两个属性,其它属性后面会谈到。

web.xml配置和之前的一样,这里不贴图了,看一下spring-servlet-config.xml的配置

技术分享图片

 

源码分析

看上图,context命名空间下的,我们不进行分析,关注<mvc:annotation-driven/>。分析事务、AOP时,我们知道了除了spring的基础命名空间外,其它标签的解析都是走parseCustomElement方法的。而对标签是解析是交给特点的解析器的。我们先在spring-webmvc的META-INF/spring.handlers方法找到MvcNamespaceHander,再在MvcNamespaceHandler找到AnnotationDrivenBeanDefinitionParser解析器。我们先看这个解析器的parse方法。

  • AnnotationDrivenBeanDefinitionParser解析<mvc:annotation-driven/>标签。这个方法太长,这里只贴出这里分析要用到的核心代码。我们阅读下面的关键代码,发现解析标签时注册了RequestMappingHandlerMapping和BeanNameUrlHandlerMapping两个处理器,后面这个处理器,有点类似struts2。这两个处理器的order值前者小于后者,并且开发时前者对所有的Controller都支持,所以在平常的开发中,走的都是前者的逻辑。所以,我们这里只对RequestMappingHandlerMapping进行分析。 
//AnnotationDrivenBeanDefinitionParser类的方法
//解析<mvc:annotation-driven/>,spring mvc的核心注解,有了它可以给我们注入映射器、适配器、异常处理器等等,这里我们关注映射器,而不用我们一个个<bean>标签地去配置了
public BeanDefinition parse(Element element, ParserContext parserContext) { ......

      RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);

        ......
        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
        ......
}

 

//MvcNamespaceUtils类的方法
//注册默认组件,这里我们关注下BeanNameUrlHandlerMapping的注册
public static void registerDefaultComponents(ParserContext parserContext, Object source) { registerBeanNameUrlHandlerMapping(parserContext, source); registerHttpRequestHandlerAdapter(parserContext, source); registerSimpleControllerHandlerAdapter(parserContext, source); }
  • 通过上过的解析过程,我们已经把RequestMappingHandlerMapping给注册到容器了。接下来就是实例化了,通过概述中的整个继承体系中,我们知道RequestMappingHandlerMapping是实现了InitialzingBean接口的,而实现接口的类是AbstractHandlerMethodMapping。而这个类的afterPropertiesSet方法是整个映射关系建立的入口,我们从这里开始分析。这里我们可以看到有以下几步
    • 遍历所有的bean
    • 如果是scope proxy则不管(这个在AOP中再补充进去),否则判断是不是handler。比较简单,就是看看类是否有@Controller或@RequestMapping注解
    • 如果这个类是handler,则会去查找这个类的所有方法
    • 最后的一个handlerMethodsInitialized方法是个空方法,不用管了
//AbstractHandlerMethodMapping类的方法
//映射关系建立的入口
public void afterPropertiesSet() { initHandlerMethods(); }
//AbstactHandlerMethodMapping类的方法
//建立映射关系
protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { beanType = getApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let‘s ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name ‘" + beanName + "‘", ex); } } if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }
//RequestMappingHandlerMapping类的方法
//判断是否为handler
protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
  • 查找Controller下所有带有@RequestMapping注解的方法,最后注册HandlerMethod
//AbstractHandlerMethodMapping类的方法
//查找并注册带HandlerMethod
protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); final Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, new MethodIntrospector.MetadataLookup<T>() { @Override public T inspect(Method method) { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } } }); if (logger.isDebugEnabled()) { logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); } for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } }
  • 查找所有带@RequestMapping的方法,并存放到Map<Method,RequestMappingInfo>中,这里我们注意下RequestMappingInfo中的patternsCondition属性就是我们解析注解中的url
//MethodIntrospector类的方法
//遍历所有的方法,包括父类中的,匹配到的则注册到map中
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) { final Map<Method, T> methodMap = new LinkedHashMap<Method, T>(); Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>(); Class<?> specificHandlerType = null; if (!Proxy.isProxyClass(targetType)) { handlerTypes.add(targetType); specificHandlerType = targetType; } handlerTypes.addAll(Arrays.asList(targetType.getInterfaces())); for (Class<?> currentHandlerType : handlerTypes) { final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType); ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() { @Override public void doWith(Method method) { Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); T result = metadataLookup.inspect(specificMethod); if (result != null) { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { methodMap.put(specificMethod, result); } } } }, ReflectionUtils.USER_DECLARED_METHODS); } return methodMap; }
//RequestMappingHandlerMapping类的方法
//判断是否有@RequestMapping注解
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
//RequestMappingHandlerMapping类的方法
//这里就是把@RequestMappingHandlerMapping中的信息封装到RequestMappingInfo对象中
protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, RequestCondition<?> customCondition) { return RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()) .customCondition(customCondition) .options(this.config) .build(); }
public RequestMappingInfo build() {
    ContentNegotiationManager manager = this.options.getContentNegotiationManager();

    PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
            this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
            this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
            this.options.getFileExtensions());

    return new RequestMappingInfo(this.mappingName, patternsCondition,
            new RequestMethodsRequestCondition(methods),
            new ParamsRequestCondition(this.params),
            new HeadersRequestCondition(this.headers),
            new ConsumesRequestCondition(this.consumes, this.headers),
            new ProducesRequestCondition(this.produces, this.headers, manager),
            this.customCondition);
}
  • 注册HandlerMethod,首先我们看到注册信息是存放在AbstractHandlerMethodMapping的mappingRegistry属性,而这个属性对应的类是AbstractHandlerMethodMapping的一个内部类,下面我们看在这个类上的注册过程。
    • 创建HandlerMethod,这个比较简单,就是把bean名字、对应是IOC容器、方法等这些属性设置到HandlerMethod中
    • 建立mapping与handlerMethod的映射
    • 查找@RequestMapping注解中的url地址(这个在上一步中已经保存在RequestMappingInfo对象中),建立url和mapping的映射关系
    • 建立mapping和MappingRegistration(这里名包含了mapping、handlerMethod、url等所有的信息)

到这里所有的映射关系都建立好的,通过url就可以找到maping信息,通过mapping信息就对找到handlerMethod、mappingRegistration等所有所有信息了。接下来,我们就来看调用过程。  

//AbstractHandlerMethodMapping类的方法
//注册处理器方法
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
//AbstractHandlerMethodMapping的内部类
//映射关系都是注册在这个类上的
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
  • 调用过程,让我们来到调用过程。之前我们分析过,调用过程入口doDispatch方法,这里只贴出我们关心的一点代码,即拿到请求对应的handler。我们看到返回的是HandlerExecutionChan对象,这个类有handler和拦截器的属性,本篇我们的关注点放到handler属性上,拦截器后面用单独的章节来讨论。
//DispatcherServlet类方法
//请求调用过程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ...... HandlerExecutionChain mappedHandler = null; ...... mappedHandler = getHandler(processedRequest); ....... }
//HandlerExecutionChain的属性
private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList;
  • 遍历所有的HandlerMapping,在上面的分析中,我们说过这里会有两个HandlerMapping对象:RequestMappingHandlerMapping和BeanNameUrlHandlerMapping对象。因为RequestMappingHandlerMapping的优先级高,会先去其中查找handler。通过后面的分析,我们会知道在RequestMappingHandlerMapping中会找到相应的handler并返回到DispatcherServlet。
//DispatcherServlet类的方法
//获取处理器链
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 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; }
//RequestMappingHandlerMapping的父类AbstractHandlerMapping的方法
//查找handler和拦截器,形成处理链
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 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 = getApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
  • 现在的过程是比较清楚的,分为下面几步
    • 从请求中解析出url
    • 根据url从之前说过的url与handlerMethod中找到拿到的handlerMethod集合
    • 选出了适合的handlerMethod返回回去,所以HandlerExecutionChain中的handler属性是一个handlerMethod对象
//AbstractHandlerMethodMapping类的方法
//根据请求获取合适的HandlerMethod对象
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); if (logger.isDebugEnabled()) { if (handlerMethod != null) { logger.debug("Returning handler method [" + handlerMethod + "]"); } else { logger.debug("Did not find handler method for [" + lookupPath + "]"); } } return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }

  //AbstractHandlerMethodMapping类的方法
  //根据url获取合适的HandlerMethod对象

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<Match>();
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // No choice but to go through all mappings...
        addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    }

    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        Collections.sort(matches, comparator);
        if (logger.isTraceEnabled()) {
            logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
                    lookupPath + "] : " + matches);
        }
        Match bestMatch = matches.get(0);
        if (matches.size() > 1) {
            if (CorsUtils.isPreFlightRequest(request)) {
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path ‘" +
                        request.getRequestURL() + "‘: {" + m1 + ", " + m2 + "}");
            }
        }
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}
public List<T> getMappingsByUrl(String urlPath) {
    return this.urlLookup.get(urlPath);
}

总结

整个流程还是比较清楚的,解析标签<mvc:annotation-driven/>、实例化RequestMappingHandlerMapping时会建立映射关系、请求时查找映射关系。接下来会接着对拦截器进行分析

参考链接

  • https://www.cnblogs.com/weknow619/p/6341395.html(ContextLoaderListener与DispatcherServlet的创建的上下文)
  • http://www.cnblogs.com/fangjian0423/p/springMVC-dispatcherServlet.html(源码分析参考博文)
  • https://blog.csdn.net/qq_21033663/article/details/52374436(解析器参考博文)

 

springmvc源码分析之拦截器

标签:代码   oca   tor   tpm   继承   解析过程   chain   nfa   extension   

原文地址:https://www.cnblogs.com/lucas2/p/9425044.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!