• SpringMVC之构造映射关系


      springmvc正是通过构造请求的模式和其对应的method,然后通过反射机制执行方法,并将结果返回。

      本篇就分析下映射关系的初始化过程

    一 关键类讲解

       HandlerMethod 

    public class HandlerMethod {
    
        /** Logger that is available to subclasses */
        protected final Log logger = LogFactory.getLog(getClass());
    
        private final Object bean;
    
        private final BeanFactory beanFactory;
    
        private final Class<?> beanType;
    
        private final Method method;//实际的method
    
        private final Method bridgedMethod;
    
        private final MethodParameter[] parameters;//入参

      它有一个非常重要的子类,  ServletInvocableHandlerMethod 

      ServletInvocableHandlerMethod这个类在HandlerAdapter对每个请求处理过程中,都会实例化一个出来(上面提到的属性由HandlerAdapter进行设置),分别对请求和返回进行处理。  (RequestMappingHandlerAdapter源码,实例化ServletInvocableHandlerMethod的时候分别set了上面提到的重要属性)

       MethodParameter 

    public class MethodParameter {
    
        private final Method method;//方法名
    
        private final Constructor<?> constructor;
    
        private final int parameterIndex;//该参数的下标,表示第几个入参
    
        private int nestingLevel = 1;
    
        /** Map from Integer level to Integer type index */
        Map<Integer, Integer> typeIndexesPerLevel;
    
        private volatile Class<?> containingClass;
    
        private volatile Class<?> parameterType;//类型
    
        private volatile Type genericParameterType;
    
        private volatile Annotation[] parameterAnnotations;
    
        private volatile ParameterNameDiscoverer parameterNameDiscoverer;
    
        private volatile String parameterName;//参数名
    
        private volatile MethodParameter nestedMethodParameter;

       RequestMappingInfo   是一个封装了各种请求映射条件并实现了RequestCondition接口的类。

      有各种RequestCondition实现类属性,patternsCondition,methodsCondition,paramsCondition,headersCondition,consumesCondition以及producesCondition,这个请求条件看属性名也了解,分别代表http请求的路径模式、方法、参数、头部等信息。

    public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
    
        private final String name;
    
        private final PatternsRequestCondition patternsCondition;//URL模式 
    
        private final RequestMethodsRequestCondition methodsCondition;//GET 还是POST
    
        private final ParamsRequestCondition paramsCondition;
    
        private final HeadersRequestCondition headersCondition;
    
        private final ConsumesRequestCondition consumesCondition;
    
        private final ProducesRequestCondition producesCondition;

       RequestMappingHandlerMapping 

      处理请求与HandlerMethod映射关系的一个类

    二 初始化过程

      我们先从命名空间开始说起  

      当要使用mvc都是这么开始的  <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/> 

      熟悉spring的都知道命名空间是怎么回事,直接到jar包的META-INF里去找

       http://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler 

      

    public class MvcNamespaceHandler extends NamespaceHandlerSupport {
    
        @Override
        public void init() {
            registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
            registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
            registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
            registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
            registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
            registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
            registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
            registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
            registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
            registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
            registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
            registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
            registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
            registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
        }
    class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
    
        public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();//默认的bean
    
        public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();//默认的bean

      

    @Override
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            Object source = parserContext.extractSource(element);
            XmlReaderContext readerContext = parserContext.getReaderContext();
    
            CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
            parserContext.pushContainingComponent(compDefinition);
    
            RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);
    
            RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
            handlerMappingDef.setSource(source);
            handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            handlerMappingDef.getPropertyValues().add("order", 0);
            handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
    
            if (element.hasAttribute("enable-matrix-variables")) {
                Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
                handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
            }
            else if (element.hasAttribute("enableMatrixVariables")) {
                Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
                handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
            }
    
            configurePathMatchingProperties(handlerMappingDef, element, parserContext);
            readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);

      上面的代码说明了,在解析   <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>  

      系统自动给容器中注册了两个BeanDefinition,分别是 RequestMappingHandlerMapping ,RequestMappingHandlerAdapter

      接下来我们就分析  RequestMappingHandlerMapping  的初始化过程

    三  RequestMappingHandlerMapping 初始化

      初始化的工作在  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)) {//拿到当前容器的所有的bean,判断是不是controller
                        detectHandlerMethods(beanName);
                    }
                }
            }
            handlerMethodsInitialized(getHandlerMethods());
        }

      

    protected void detectHandlerMethods(final Object handler) {//handler就是实际的controller实现类
            Class<?> handlerType = (handler instanceof String ?
                    getApplicationContext().getType((String) handler) : handler.getClass());
            final Class<?> userType = ClassUtils.getUserClass(handlerType);
    
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType, //这里的methods的结构是key是Method,而Value是一个RequestMappingInfo 
                    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);
            }
        }

      我们看一下  RequestMappingHandlerMapping 的

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

      

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

      入参 AnnotatedElement 是  public com.suning.rdrs.admin.controller.ResponseResult com.suning.rdrs.admin.controller.audit.RdrsAuditCallbackController.manualAudit(java.lang.String) ,类型就是java反射的Method。

       AnnotatedElementUtils.findMergedAnnotation 的作用就是解析Method带有的RequestMapping注解

      requestMapping的返回值是  @org.springframework.web.bind.annotation.RequestMapping(name=, value=[/alarm/summary], path=[/alarm/summary], method=[GET], params=[], headers=[], produces=[], consumes=[]) 

      我这里的condition是null,因为我没有配特殊模式

      然后就是构造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();
        }

      整个过程最后我们得到  Map<Method, T> methods  key是Method,Value是RequestMappingInfo

      让我们回到  AbstractHandlerMethodMapping.detectHandlerMethods 看看剩下的部分

    for (Map.Entry<Method, T> entry : methods.entrySet()) {
                Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
                T mapping = entry.getValue();
                registerHandlerMethod(handler, invocableMethod, mapping);
            }

       AbstractHandlerMethodMapping 中的方法

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
            this.mappingRegistry.register(mapping, handler, method);
        }

      

    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); //key是RequestMappingInfo对象,Value是实际的方法
    
                    List<String> directUrls = getDirectUrls(mapping);
                    for (String url : directUrls) {
                        this.urlLookup.add(url, mapping); //保存url和mapping关系注意 mapping是一个RequestMappingInfo对象
                    }
    
                    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();
                }
            }

      以上就是分析怎么把请求url和对应的处理方法关联起来的,这里面有很多细节比如RequestMappingInfo在构建中,有些用户会写很复杂的url模式,比如带通配符的,通配符还有很多钟模式等等。本文要讨论的是原理,哪些字符串解析就不管他了。

      

  • 相关阅读:
    边缘计算的爆发为安防全产业带来了怎样的变化?
    Kali卸载AWVS的方法
    C++最简打开网页的方法
    C# 打开指定文件或网址
    C# 如何获取某用户的“我的文档”的目录
    基于Debian的linux系统软件安装命令
    C#的基础知识
    MYSQL语句中的增删改查
    将博客搬至CSDN
    【易懂】斜率DP
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14096412.html
Copyright © 2020-2023  润新知