zoukankan      html  css  js  c++  java
  • SpringMVC源码阅读HandlerMapping初始化-RequestMappingHandlerMapping(六)

    我们常用的使用方式就是@Contorller 和@RequsetMappig方式 就是通过RequestMappingHandlerMapping实现

    类图

     AbstractHandlerMapping上一篇已经说过了 我们主要看红线框起来的

    AbstractHandlerMethodMapping

    实现了InitializingBean接口 spring托管初始化 就会调用这个方法

    afterPropertiesSet

    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet

     public void afterPropertiesSet() {
            this.initHandlerMethods();
        }
    
        protected void initHandlerMethods() {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Looking for request mappings in application context: " + this.getApplicationContext());
            }
    
            //获得容器中所有的bean的名字
            String[] beanNames = this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.obtainApplicationContext(), Object.class) : this.obtainApplicationContext().getBeanNamesForType(Object.class);
            String[] var2 = beanNames;
            int var3 = beanNames.length;
    
            for(int var4 = 0; var4 < var3; ++var4) {
                String beanName = var2[var4];
                //不是scopedTarget开头的
                if (!beanName.startsWith("scopedTarget.")) {
                    Class beanType = null;
    
                    try {
                        //获得bean对象对应类的class
                        beanType = this.obtainApplicationContext().getType(beanName);
                    } catch (Throwable var8) {
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Could not resolve target class for bean with name '" + beanName + "'", var8);
                        }
                    }
                    //<1>判断是否是Handler类 比如判断是否打上了@Contorller 由子类实现注解
                    if (beanType != null && this.isHandler(beanType)) {
                        //<2>解析BeeanMethods
                        this.detectHandlerMethods(beanName);
                    }
                }
            }
            //空实现 传入解析号的HandleMethods
            this.handlerMethodsInitialized(this.getHandlerMethods());
        }

    <1>isHandler

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler

      protected boolean isHandler(Class<?> beanType) {
            //是否打上了Controller 注解 或者是否打上了RequestMapping  如果父类打上了也会被扫描到 比如Controller打的@Service注解 父类打的@Controller
            return AnnotationUtils.findAnnotation(beanType, Controller.class) != null || AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null;
        }

    <2>detectHandlerMethods

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#detectHandlerMethods

        protected void detectHandlerMethods(Object handler) {
            //如果是传入的beanname则从容器里面获得指定bean的Type如果是对象 则直接获取classs
            Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
            if (handlerType != null) {
                //暂时不造干嘛 正常情况是=handlerType
                Class<?> userType = ClassUtils.getUserClass(handlerType);
                /**
                 * 这里会遍历获取class的公共的方法 如果 是重写的父类方法 则获取父类的method
                 * 筛选查打上了@RequstMapping的方法
                 */
                Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
                    try {
                        /**
                         * <3>这里this 是AbstractHandlerMethodMapping   getMappingForMethod是抽象方法由子类实现主要用于判断是否是打上了@RequestMapping的方法
                         * 并封装成RequestMappingInfo 封装了完整路由信息 如:/user/login
                         * 如果不死 则返回null
                         */
                        return this.getMappingForMethod(method, userType);
                    } catch (Throwable var4) {
                        throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
                    }
                });
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Mapped " + methods.size() + " handler method(s) for " + userType + ": " + methods);
                }
    
                methods.forEach((method, mapping) -> {
    <4> Method invocableMethod
    = AopUtils.selectInvocableMethod(method, userType); /** * <5>注册到mappingRegistry invocableMethod为方法 mapping为完整路由信息 */ this.registerHandlerMethod(handler, invocableMethod, mapping); }); } }

    到这里mappingRegistry 就封装了所有handler和路由信息

    RequestMappingHandlerMapping

    <3>getMappingForMethod

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#detectHandlerMethods

    ->

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod

     protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
            //<6>获取方法上的@RequstMapping注解并封装成RequestMappingInfo
            RequestMappingInfo info = this.createRequestMappingInfo(method);
            if (info != null) {
                //<6>这里就是对应获取Contorller上面的@RequstMapping注解
                RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
                if (typeInfo != null) {
                    //这里主要是2个info合并成一个 就是url拼接 controler的RequstMapping注解和方法的@RequestMapping注解拼接
                    info = typeInfo.combine(info);
                }
                /**
                 *  private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap();
                 *  这里是利用成员变量 判断是否给此handlerType 加上指定前缀 没发现杂用
                 */
              
                String prefix = this.getPathPrefix(handlerType);
                //如果有配置加上前缀
                if (prefix != null) {
                    info = RequestMappingInfo.paths(new String[]{prefix}).build().combine(info);
                }
            }
    
            return info;
        }

    <6>createRequestMappingInfo

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#detectHandlerMethods

    ->

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod

    ->

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(java.lang.reflect.AnnotatedElement)

      @Nullable
        private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
            //获得requestMapping注解对象
            RequestMapping requestMapping = (RequestMapping) AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
            RequestCondition<?> condition = element instanceof Class ? this.getCustomTypeCondition((Class)element) : this.getCustomMethodCondition((Method)element);
            //封装成RequestMappingInfo
            return requestMapping != null ? this.createRequestMappingInfo(requestMapping, condition) : null;
        }

    <5>registerHandlerMethod

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#detectHandlerMethods

    ->

    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod

     protected void registerHandlerMethod(Object handler, Method method, T mapping) {
            this.mappingRegistry.register(mapping, handler, method);
        }
        public void register(T mapping, Object handler, Method method) {
            //ReentrantReadWriteLock 写锁
            this.readWriteLock.writeLock().lock();
    
            try {
                //将handler 和method封装为HandlerMethod  内部维护了控制器 控制器方法  参数等信息
                HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
                this.assertUniqueMethodMapping(handlerMethod, mapping);
                this.mappingLookup.put(mapping, handlerMethod);
                List<String> directUrls = this.getDirectUrls(mapping);
                Iterator var6 = directUrls.iterator();
    
                while(var6.hasNext()) {
                    String url = (String)var6.next();
                    //url为路由url  mapping为RequestMapppingInfo
                    this.urlLookup.add(url, mapping);
                }
    
                String name = null;
                if (AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {
                    name = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);
                    this.addMappingName(name, handlerMethod);
                }
    
                CorsConfiguration corsConfig = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    this.corsLookup.put(handlerMethod, corsConfig);
                }
                /**
                 * key为Mapping info 存储路由信息
                 * value为MappingRegistration  内部封装了HandlerMethod和mappingInfo
                 */
                this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directUrls, name));
            } finally {
                this.readWriteLock.writeLock().unlock();
            }
    
        }

    总结

    1.我们常用的注解配置是通过实现spring 的InitializingBean接口进行初始化

    2.会活动容器里面所有的bean 3.判断是Handle(是否打上了@Contorller注解)

    3.遍历这些Handle所有的方法遍历是否是方法Handle(是否打上了@RequestMapping)

    4封装成HandleMappingInfo返回 保存了路由信息

    5.最终会解析成HandleMethod保存起来 保存了控制器信息 方法信息 对象工厂信息 通过HandleMappingInfo为key  HandleMethod为Value保存起来

    6.后续就可以快速找到对一个的Handle处理

  • 相关阅读:
    Sublime Text 3 绝对神器
    spring 笔记3: Spring 多环境配置文件切换
    elk-logstash: window下指定jdk目录
    通过slf4j/log4j的MDC/NDC 实现日志追踪
    spring 笔记2:Spring MVC : Did not find handler method for 问题的解决
    mysql一机多实例安装记录
    Java:通过反射复制父类字段到子类。
    mybatis研究:select性能对比
    spring " expected single matching bean but found 2" 问题一例。
    数组去重复
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12218060.html
Copyright © 2011-2022 走看看