Spring MVC 收集Controller

作者: 丶含光 | 来源:发表于2019-11-30 16:29 被阅读0次

    EnableWebMvc

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(DelegatingWebMvcConfiguration.class)
    public @interface EnableWebMvc {
    }
    

    EnableWebMvc上使用@Import导入了类DelegatingWebMvcConfiguration,含义为在容器扫描bean时,会将导入的类一起处理。
    导入的DelegatingWebMvcConfiguration与父类WebMvcConfigurationSupport

    DelegatingWebMvcConfiguration
    @Configuration
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
    WebMvcConfigurationSupport
        @Bean
        public RequestMappingHandlerMapping requestMappingHandlerMapping() {
            RequestMappingHandlerMapping handlerMapping = createRequestMappingHandlerMapping();
            handlerMapping.setOrder(0);
            handlerMapping.setInterceptors(getInterceptors());
            handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
            handlerMapping.setCorsConfigurations(getCorsConfigurations());
    
            PathMatchConfigurer configurer = getPathMatchConfigurer();
            if (configurer.isUseSuffixPatternMatch() != null) {
                handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
            }
            if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
                handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
            }
            if (configurer.isUseTrailingSlashMatch() != null) {
                handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
            }
            UrlPathHelper pathHelper = configurer.getUrlPathHelper();
            if (pathHelper != null) {
                handlerMapping.setUrlPathHelper(pathHelper);
            }
            PathMatcher pathMatcher = configurer.getPathMatcher();
            if (pathMatcher != null) {
                handlerMapping.setPathMatcher(pathMatcher);
            }
    
            return handlerMapping;
        }
    

    DelegatingWebMvcConfiguration上的注解@Configuration,说明其为完全配置类,从父类继承的requestMappingHandlerMapping方法上的注解@Bean,说明会将RequestMappingHandlerMapping托管给容器,beanName为方法名。

    RequestMappingHandlerMapping关系图
    RequestMappingHandlerMapping的基类实现了InitializingBean方法,在容器初始化该bean时,会执行其初始化方法afterPropertiesSet
    RequestMappingHandlerMapping
        @Override
        public void afterPropertiesSet() {
            this.config = new RequestMappingInfo.BuilderConfiguration();
            this.config.setUrlPathHelper(getUrlPathHelper());
            this.config.setPathMatcher(getPathMatcher());
            this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
            this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
            this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
            this.config.setContentNegotiationManager(getContentNegotiationManager());
    
            super.afterPropertiesSet();
        }
    ...
        @Override
        protected boolean isHandler(Class<?> beanType) {
            return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                    AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
        }
    

    父类AbstractHandlerMethodMappingafterPropertiesSet方法

    AbstractHandlerMethodMapping
        @Override
        public void afterPropertiesSet() {
            initHandlerMethods();
        }
    ...
        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());
        }
    

    从容器中获取了所有的Object类型的bean,意味着拿到了所有bean,遍历bean使用isHandler判断。拥有Controller注解或者RequestMapping注解的视为Handler,对HandlerdetectHandlerMethods方法内选出其内部可以处理请求的方法。

    RequestMappingHandlerMapping
        @Override
        protected boolean isHandler(Class<?> beanType) {
            return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                    AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
        }
    
    AbstractHandlerMethodMapping
        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);
            }
        }
    

    MethodIntrospectorselectMethods方法

    MethodIntrospector
        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;
        }
    

    获取当前处理的bean和其接口类,对他们的所有方法,在inspect中做处理,最终的处理在getMappingForMethod方法中

    RequestMappingHandlerMapping
        @Override
        protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
            RequestMappingInfo info = createRequestMappingInfo(method);
            if (info != null) {
                RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
                if (typeInfo != null) {
                    info = typeInfo.combine(info);
                }
            }
            return info;
        }
    
    

    可以看到传入的参数为当前正在处理的方法和类,先获取方法的RequestMappingInfo,如果不为null再获取类的RequestMappingInfo,然后将方法与类的RequestMappingInfo相连接,构成最终的RequestMappingInfo。(例如方法上的mapping为/method,类上的mapping为/controller,那么最终的请求路径为/controller/method)

    RequestMappingHandlerMapping
        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);
        }
    

    尝试从元数据中获取RequestMapping注解,再判断当前处理的是method还是class,最终创建RequestMappingInfo

    回到AbstractHandlerMethodMappingdetectHandlerMethods方法中,MethodIntrospector.selectMethods方法执行完成之后的返回值是一个Map,包含所有能处理请求的方法。registerHandlerMethod方法中对这些方法进行注册。

    AbstractHandlerMethodMapping
        protected void registerHandlerMethod(Object handler, Method method, T mapping) {
            this.mappingRegistry.register(mapping, handler, method);
        }
    内部类 MappingRegistry
            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();
                }
            }
    

    可以看到将内部类MappingRegistry中,使用handlermethod创建了HandlerMethod,然后将其与RequestMappingInfo按照KV的形式保存在MapmappingLookup中。然后拿到mapping内的路径,按照url-mapping的形式保存在MultiValueMap(一个Key对应多个Value)urlLookup中。最后在Mapregistry中保存了RequestMappingInfoMappingRegistration的映射。
    至此,RequestMappingHandlerMapping的初始化方法执行完成。

    总结:

    • 声明注解@EnableWebMvc,会将注解上导入的类DelegatingWebMvcConfiguration托管给容器
    • DelegatingWebMvcConfiguration上声明了注解@Configuration表示其是一个完全配置类,拥有配置Bean的能力。
    • 它的父类WebMvcConfigurationSupport中使用@Bean标注了方法requestMappingHandlerMapping,表示想要将该方法的返回值托管给容器,最终将类RequestMappingHandlerMapping托管给容器。
    • 在容器初始化类RequestMappingHandlerMapping时,由于它的基类实现了接口InitializingBean,所以它的初始化方法afterPropertiesSet会被调用。
    • 最终在基类AbstractHandlerMethodMappingafterPropertiesSet方法中调用了initHandlerMethods方法。完成了对所有包含@Controller注解或者@RequestMapping注解的类的处理,将这些类中的带有@RequestMapping注解的方法与其类合并为完全的RequestMappingInfo,保存在AbstractHandlerMethodMapping的属性中。

    相关文章

      网友评论

        本文标题:Spring MVC 收集Controller

        本文链接:https://www.haomeiwen.com/subject/cpyowctx.html