zoukankan      html  css  js  c++  java
  • 【Spring源码解析】—— 结合SpringMVC过程理解IOC容器初始化之注解部分探究

    前面的文章写了xml中直接配置bean进行IOC的过程解析,接下来会针对注解进行IOC容器初始化的过程解析

    因为会与之前的内容存在部分重叠,因此会针对相同的部分简略带过,针对不同的部分做重点说明:

    一、Xml的配置和代码中的注解配置:

    applicationContext.xml配置添加:

    <context:component-scan base-package="cn.lx.controller" />

     

    代码中配置注解修改:

    @Controller
    
    public class TestController {
    
        @RequestMapping("/test.form")
    
        public void execute(){
    
            return ;
    
        }
    
    }

    二、详解:

    入口部分:ContextLoaderListener类中的contextInitialized,进入到ContextLoader类的initWebApplicationContext

    方法中,该方法中执行的关键方法是:configureAndRefreshWebApplicationContext()进入到ConfigurableWebApplicationContext

    类的实例wac.refresh()调用中,至此进入到具体接下来的load阶段了

    load过程:

    进入到AbstractApplicationContext类的refresh()类中,之后进入到ObtainFreshBeanFactory()方法中,一路往下跟进到实现方法,之后进入到:AbstractRefreshableApplicationContext类中的refreshBeanFactory()方法,在此方法中,进行CreateFactory()会得到DefaultListableBeanFactory类的一个实例beanFactory,之后会作为方法参数传入到loadBeanDefinitions(beanFactory)中,这里其实就是能明显看到有load字眼啦,继续一步步往下跟进,进入到真正做事情的方法就是doLoadBeanDefinitions中,这里会生成一个BeanDefinitionDocumentReader类的实例,之后通过该实例调用方法registerBeanDefinitions,依然是要进入到真正做事的doRegisterBeanDefinitions方法中,至此就马上到了process的部分了,在这个部分会针对传入的元素进行解析前、中、后的处理,我们进入到解析中的方法:parseBeanDefinitions(root, this.delegate),在解析的方法中,会判断如果是bean相关namespace的,则会parseDefaultElement,因为这里是注解的形式,因此其nameSpace不为默认的bean相关的,而是Context的,因此进入到:delegate.parseCustomElement(ele)中,接下来就是具体基于注解进行解析的部分了,即process过程

    具体可见下方代码:

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
       //判断节点是否属于同一命名空间,是则执行后续的解析
       if (delegate.isDefaultNamespace(root)) {
          NodeList nl = root.getChildNodes();
          for (int i = 0; i < nl.getLength(); i++) {
             Node node = nl.item(i);
             if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                   parseDefaultElement(ele, delegate);
                }
                else {
                   //注解定义的Context的nameSpace进入到这个分支中
                   delegate.parseCustomElement(ele);
                }
             }
          }
       }
       else {
          delegate.parseCustomElement(root);
       }
    }
    
     

    process过程:

    具体process过程做了哪些事情呢?可分为两个步骤来说明,首先根据ele的定义得到key,通过key返回对应的namespaceUri,之后根据namespaceUri的解析得到一个NameSpaceHandler的实例handler,之后由具体实现了NameSpaceHandler接口的类NameSpaceHandlerSupport类进行的方法调用,即parse方法的调用,即离具体的解析更进一步啦~

    可见下方代码注释部分:

    //常规解析方法
    @Nullable
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
       //获取namespaceUri,例如xml中配置的如果是<context:componet-scan base-packages:xxx>
       //这里的ele会得到component-scan,并且值为null,namespaceUri为 http://www.springframework.org/schema/context
       String namespaceUri = getNamespaceURI(ele);
       if (namespaceUri == null) {
          return null;
       }
       //基于namespaceUri得到handler,得到handler之后,不同的handler实现了parse方法,到具体的parse去进行调用和处理
       //因为Handler为:org.springframework.context.config.ContextNamespaceHandler
       //在resolve的init操作中直接将component-scan的key和对应的ComponentScanBeanDefinitionParser的实例放入到了parser的map中
       NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
       if (handler == null) {
          error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
          return null;
       }
       //NameSpaceHandler是接口,具体这里调用的就是得到的handler的实际类型,通过它进行parse调用
       //实现该Handler的类是:NamespaceHandlerSupport
       return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

    下面继续说明一下通过namespaceUri的resolve具体是如何实现的的?接下来是对这里的详细说明:

    首先this.readerContext.getNamespaceHandlerResolver()返回内容为:

    public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
       return this.namespaceHandlerResolver;
    }

    注意这里定义的是final,final的方法不能被重写,因为返回的是NamespaceHandlerResolver,发现其是一个接口,因此直接找实现类,点击resolve找到对应的实现类DefaultNamespaceHandler(注意这里又体现了,最终真的去做事情的,很多都会被命名为Defaultxxx类,或者Simplexxx类)对该方法的实现,resolve中所做事项就是判定是否已有可用解析类,若无则进行初始化init操作,并且返回handler实例

    public NamespaceHandler resolve(String namespaceUri) {
          //先通过handlerMappings的配置进行get获取,针对传入的namespaceUri是否存在handler可供使用
          Map<String, Object> handlerMappings = getHandlerMappings();
          //注意:这里的从mapping中得到的key为Object的,因为这里可能为各种不同的具体Handeler,namespaceUri不同,则其value不同
          //这里根据namespaceUri为context的值:org.springframework.context.config.ContextNamespaceHandler,发现不为null
          Object handlerOrClassName = handlerMappings.get(namespaceUri);
          //if判断不为空,跳过此逻辑
          if (handlerOrClassName == null) {
             return null;
          }
          //判断不为NamespaceHandler的实例
          else if (handlerOrClassName instanceof NamespaceHandler) {
             return (NamespaceHandler) handlerOrClassName;
          }
          else {
             //直接将前面的“org.springframework.context.config.ContextNamespaceHandler”转成String类型的
             String className = (String) handlerOrClassName;
             try {
                //生成类
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                   throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                         "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                }
                //类的实例化,如果是前一个函数中的<context:Component-scan>这里得到的Handler为:
             //org.springframework.context.config.ContextNamespaceHandler
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                //因为上面是ContextNamespaceHandler,将N种不同的key对应的具体的parser进行new之后作为key放到parsers的map中
                //这里针对key为"component-scan",直接new的实例就是:ComponentScanBeanDefinitionParser
                namespaceHandler.init();
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
             }
             catch (ClassNotFoundException ex) {
                throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
                      "] for namespace [" + namespaceUri + "]", ex);
             }
             catch (LinkageError err) {
                throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
                      className + "] for namespace [" + namespaceUri + "]", err);
             }
          }
       }

    之后通过handler实例进行parse方法调用,实现类为:NamespaceHandlerSupport,其中parse方法如下,就是找到真正的parser,从之前的handler的init操作所put的map中将需要解析的key对应的value即解析类实例取出,然后进行真正的解析操作,在parse中会将bean定义注册代理给scanner类实例,之后通过scanner.doScan()方法调用真正完成bean的解析和注册到容器中

    public BeanDefinition parse(Element element, ParserContext parserContext) {
       //确定是什么parser,之前已经存储在Handler的parsers的map中
       //find就是找到对应element对应的具体key的具体解析类
       BeanDefinitionParser parser = findParserForElement(element, parserContext);
       //根据具体解析类,直接进行对应的解析调用
       return (parser != null ? parser.parse(element, parserContext) : null);
    }

    就进入到parser.parse()部分,因为对应key为Component-scan对应的解析类为ComponentScanBeanDefinitionParser类,进入到此类的parse方法中:

    public BeanDefinition parse(Element element, ParserContext parserContext) {
       //根据element为“base=package”得到基础包路径
       String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
       //解析basePacakge的String值,将占位符前后缀都去掉
       basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
       //这里其实就是对String的basePackage进行解析,最终得到:
       //按照分隔符将多个路径转换成数组,并去掉空格以及换行符等
       String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
             ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    
       // Actually scan for bean definitions and register them.
       //进行beandefinition的扫描并注册
       //将bean定义注册代理给scanner类处理
       ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
       //doScan的调用
       Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
       registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    
       return null;
    }

    进入到比较重要的部分:ClasspathBeanDefinitionScanner类的doScan()方法,该方法主要做的事情是:针对xml中所做的配置<context:component-scan base-package="cn.lx.controller" />,根据base-package的package路径下的class文件,进行遍历操作,根据其是否具备注解定义,得到beanDefinition的候选集合,针对候选集中的每一个beanDefinition,进行beanName的生成,并且针对是否属于AbstractBeanDefinition和AnnotatedBeanDefinition,进行相应的属性设置,之后会通过beanName获取是否已经容器中是否已经存在此beanName,若无则直接返回true,表示需要进行后续注册操作,即进入到了register的过程

    其中doScan的主要方法及注释如下:

    //针对xml中所做的配置<context:component-scan base-package="cn.lx.controller" />
    //根据base-package的package路径下的class文件,进行遍历操作
    //根据其是否具备注解定义,得到beanDefinition的候选集合
    //针对候选集中的每一个beanDefinition,进行beanName的生成
    //并生成BeanDefinitionHolder,进行scopeProxyMode的设置,之后进行register操作
    //和非注解形式的合并到一路上了,后续的注册操作
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
       Assert.notEmpty(basePackages, "At least one base package must be specified");
       Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
       //针对basePackages中的每一项basePackage做循环
       for (String basePackage : basePackages) {
          //得到候选BeanDefinition集合,注意这里如果没有@Controller等注解样式的是不会被加入到候选集中
          Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
          //针对集合中的每一个候选项BeanDefinition进行详细的解析和处理
          for (BeanDefinition candidate : candidates) {
             //这里的生成内容会基于scope的字符进行解析,会得到:
             //scopedName和scopedProxyMode,其中scopeName不单独说明的话,则默认为singleton,ProxyMode为No
             ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
             //基于解析获得scope的性质给candidate设值
             candidate.setScope(scopeMetadata.getScopeName());
             //获取beanName,会走两步判断,有指定beanName的会直接赋值返回,否则会build默认的name,即shortName,例如:
             //cn.lx.controller.LoginController,则会取LoginController,并且会将首字母小写,最终形式是:loginController
             String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
             //判断beanDefinition的类型
             if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
             }
             //基于注解类型的BeanDefinition
             //其实没有看AbstractBeanDefinition和AnnotatedBeanDefinition有何不同,写具体文章的时候要看
             //AnnotatedBeanDefinition是接口
             //其中在candidates的生成方法findCandidateComponents()进行candidates的候选集的生成中
             //ScannedGenericBeanDefinition是实现了AnnotatedBeanDefinition接口的
             //返回的beanDefinition本身就是就是实现了这个接口的,因此必然满足instanceof
             if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
             }
             //对beanName和候选项的bean进行校验,以确定是否要进行注册,还是可能与已有bean存在冲突
             if (checkCandidate(beanName, candidate)) {
                //BeanDefinition的持有者
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                //设置ScopeProxyMode,若为设置,则默认为No,直接返回beanDefinition
                definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                //真正标识着下一步就是register的操作了,只是要一步步走到DefaultListableBeanFactory类实例中的注册方法的调用
                registerBeanDefinition(definitionHolder, this.registry);
             }
          }
       }
       return beanDefinitions;
    }

    register过程:

    register过程无所谓是针对有无注解的情况,都是相同的逻辑,通过ClassPathBeanDefinitionScanner类的registerBeanDefition方法,其实调用的是BeanDefinitionReaderUtils类的静态方法registerBeanDefinition,之后再跟进到此静态方法中,就进入到了DefaultListableBeanFactory类的register操作,就会进行真正的map操作了

    protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
       BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
    }

    三、过程时序图(梳理主要调用逻辑,涉及类和方法,可通过下载大图查看)

  • 相关阅读:
    Django_Setings
    python之event【事件】
    python之递归锁【Rlock】
    python之信号量【Semaphore】
    python的线程锁
    python的多线程和守护线程
    python的错误类型和异常处理
    python之ftp作业【还未完成】
    python之socket运用之传输大文件
    python之socket运用之执行命令
  • 原文地址:https://www.cnblogs.com/keke-xiaoxiami/p/10773017.html
Copyright © 2011-2022 走看看