前言

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

概述

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

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

java如何寻找controller是被哪个前端调用的 springmvc如何找到controller_spring

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

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

java如何寻找controller是被哪个前端调用的 springmvc如何找到controller_spring_02

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

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

java如何寻找controller是被哪个前端调用的 springmvc如何找到controller_mvc_03

 

源码分析

看上图,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时会建立映射关系、请求时查找映射关系。接下来会接着对拦截器进行分析