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

 

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

posted on 2019-05-08 14:14  可可_小虾米  阅读(1041)  评论(0编辑  收藏  举报

导航