Spring核心源码(二)类解析处理

invokeBeanFactoryPostProcessors(上)


protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    //这里的参数getBeanFactoryPostProcessors()讲道理很容易误导大家,我在调试的时候发现它什么都没返回,注意他们的区别,list和map
    //回到之前我所以提到的实现了BeanFactoryPostProcessor的Config类,按道理应该是有的,但是其实它拿到的是一个list,
    //而我们是放在map里面的,所以这里它只是拿到程序员自己手动add的,注意注解方式也是拿不到的
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
  }

看这里的参数getBeanFactoryPostProcessors()讲道理很容易误导人,我在调试的时候发现它什么都没返回,注意他们的区别,

image

image

回到之前我所以提到的4个后置处理器类使用的map去存起,所以不是同一个类,而我们的getBeanFactoryPostProcessors()只是拿到程序员自己手动add的,如:

image

,这里会有个坑,如果读者尝试在我们的Main函数加上这段代码:

   public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext=
        new AnnotationConfigApplicationContext(AppConfig.class);
    annotationConfigApplicationContext.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        
      }
    });

  }

然后继续调试getBeanFactoryPostProcessors(),得出的结果会是多少呢,其实还是0,至于为什么,我们换一种写法


 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.regisrer(Appconfig.class);
annotationConfigApplicationContext.addBeanFactoryPostProcessor();
annotationConfigApplicationContext.refresh();

再次调试getBeanFactoryPostProcessors(),得出结果为1,相信读者应该有多感悟。搞明白这个之后我们继续进入invokeBeanFactoryPostProcessors

这个方法,

public static void invokeBeanFactoryPostProcessors(
      ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
    Set<String> processedBeans = new HashSet<>();
        //注意这里的beanFactory是最基本的子类,这个类包含了如何对bean注册,创建,维护等功能,所以一定会进去
    if (beanFactory instanceof BeanDefinitionRegistry) {
      BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

      //这里定义两个list是为了区分子类是实现了BeanFactoryPostProcessor,而且这里是装的程序员自己添加的
      // 还是BeanDefinitionRegistryPostProcessor,因为这两个接口是父子关系,所以功能有所区别
      List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
      List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
            //这里肯定为空,前面已经说了,我们这里是程序员自己add的,而我们并没有
      for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
        if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
          BeanDefinitionRegistryPostProcessor registryProcessor =
              (BeanDefinitionRegistryPostProcessor) postProcessor;
          registryProcessor.postProcessBeanDefinitionRegistry(registry);
          registryProcessors.add(registryProcessor);
        }
        else {
          regularPostProcessors.add(postProcessor);
        }
      }

首先遍历beanFactoryPostProcessors,注意这个参数是我们传进来,是从getBeanFactoryPostProcessors()获取的,因此一般情况我们不会手动设置,所以这里都会为空,我们往下

image

在这段代码同样是获取后置处理器,只不过获取的来源不同,还记得我们之前说过的那4个后置处理器吗?其中有一个Spring的关键类ConfigurationClassPostProcessor,不知道读者还是否记得这个类,这里Spring就会把它取出来,我们Dubug调试一下

image

获取这个ConfigurationClassPostProcessor类后,Spring会合并之前两个循环的后置处理器,然后到主干方法invokeBeanDefinitionRegistryPostProcessors

image

我们在跟踪进去:

image

看第二句代码String[] candidateNames = registry.getBeanDefinitionNames();这里会拿出6个Name,添加到configCandidates里,看调试结果回想我在前文所提出的注册的后置处理器就明白为什么是6个,其目的就是为了找出我们的配置类,如果找到就标记成full,有些读者可能了解Spring的@configuration注解是配置类的意思,但不知道读者是否了解过加和不加这个注解的作用是什么,这里我们把@configuration注解注释掉,尝试运行会发现结果并不会报错,

image

至于为什么Spring搞这么个注解,笔者会在后文对@configuration的作用以及源码实现进行详细介绍,我们继续回到代码:

image

这里我们就拿到了我们的配置类,既然拿到了配置类,那么下一步就对配置类进行解析,看代码:

image

看到这句代码 parser.parse();这句代码是Spring解析的核心代码,我们点进去

image

 

这里判断需要解析的BeanDefinition是那种类型,这里回到我们的入口

image

,我们注册的是AppConfig配置类,但其实我们还可以配置普通类,比如我们直接将我们的User类放进去

image

随后我们在调试parse->processConfigurationClass->

image

-> doProcessConfigurationClass(configClass, sourceClass);

image

看第一段代码,如果我们的配置类有标识了@Component 这样的类

如果有就会返回ture,但可以看到我们并没有标志@Component,缺能够进入这个判断,看下面代码:

image

,看到这里想到大家应该明白了吧。好了我们回到代码进入->processMemberClasses 进行分析,我们先在配置类添加一段代码

image

 

image

,可以看到已经找到这么一个类,这段代码我们点到为止,因为下面的配置类信息解析包含了这里的设计思想,所以这里不重点分析。

image

首先取出我们的扫描注解

image

然后执行 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan,
sourceClass.getMetadata().getClassName());这段代码,这里就是解析的核心代码,我们跟进去,

image

看第一段代码new ClassPathBeanDefinitionScanner,这里读者可以回顾下我们在Spring源码第三章执行构造器提到过一个scanner,笔者曾说那个scanner并不是Spring在解析时内部使用的扫描器,而当前笔者已经给出证明,当然之前定义的this.scanner有什么作用,笔者猜测可能是交给外部工程师自己操作的吧,好了话不过说,我们回到代码,接着Spring又拿到了generatorClass,随后给scanner赋予一些属性,最后来到核心代码

image

我们进入doscan方法

image

这里我们看到在经过了findCandidateComponents(basePackage);这个方法后,Spring就已经拿到我了我们定义的注解类,我们继续进入

image

 

image

可以看出Spring真正解决包扫描结束是采用的ASM字节码提取技术,笔者对此也是一知半解,所以就不多做介绍了,有兴趣的读者们可以自行深入研究,这里我就不做文章了,我们还是回到代码,下面的代码不太重要,因此Spring拿到扫描出来的类后就返回,好的我们继续回到代码

image

在对普通注解类解析完后,会将普通类拿出来作为当做配置类继续递归解析,当然通常都会跳过,然后开始对@import注解解析,有些读者可能还没见过这个@import类,我们看下@Import注解类

image

,它的作用很清晰,目的就是向Spring容器注册类,看下实例:

image

这样就和在MyImportBeanDefinitionRegistrar加上@Component是一样效果,这里源码的细节我带着大家细看了,其目的就是解析@import三种类:普通类,ImportSelector,ImportBeanDefinitionRegistrar。这里我将笔记注释贴出来,大家自行理解:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
      return;
    }

    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
      this.importStack.push(configClass);
      try {
        for (SourceClass candidate : importCandidates) {
          //这里是处理ImportSelector,转换成bd,放入一个集合
          if (candidate.isAssignable(ImportSelector.class)) {
            // Candidate class is an ImportSelector -> delegate to it to determine imports
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
            ParserStrategyUtils.invokeAwareMethods(
                selector, this.environment, this.resourceLoader, this.registry);
            //这里是否是延迟执行,如果目标对象实现的是DeferredImportSelector,就表示需要延迟
            if (selector instanceof DeferredImportSelector) {
              this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
            }
            else {
              //这里是递归,因为Import的类可能还有Import,如果没有会递归到下面的普通类放入集合
              String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
              Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
              processImports(configClass, currentSourceClass, importSourceClasses, false);
            }
          }
          //ImportBeanDefinitionRegistrar这个是Import的第二种类,实现可以直接拿到注册器
          else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // Candidate class is an ImportBeanDefinitionRegistrar ->
            // delegate to it to register additional bean definitions
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
            ParserStrategyUtils.invokeAwareMethods(
                registrar, this.environment, this.resourceLoader, this.registry);
            //将实现了ImportBeanDefinitionRegistrar的子类放进importBeanDefinitionRegistrars,后面spring会对其处理
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
          }
          else {
            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
            // process it as an @Configuration class
            //普通import类和ImportSelector都放进这个configurationClasses
            this.importStack.registerImport(
                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            processConfigurationClass(candidate.asConfigClass(configClass));
          }
        }
      }
      catch (BeanDefinitionStoreException ex) {
        throw ex;
      }
      catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
            "Failed to process import candidates for configuration class [" +
            configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
        this.importStack.pop();
      }
    }
  }

读者这里重点对ImportBeanDefinitionRegistrar的作用进行分享,看下面实例:

image

如果我们继承了ImportBeanDefinitionRegistrar,那么我们就可以拿到BeanDefinitionRegistry,并就可以手动的向Spring添加一个 BeanDefinition,我们调试一下

image

当前我们的BeanDefinitionMap是11个,等到我们执行完这段代码后就会变成12个,看下图:

image

,我们改变了BeanDefinitionMap,而BeanDefinitionMap在Spring创建对象起到了绝对作用,所以我们就改变了Bean的创建,而这就是扩展Spring的一个接口之一,后期我们有机会的话我们会重点分享Spring-Mybatis和Spring-AOP是如何利用这些接口拓展的。
在对@Import注解解析完之后,又开始对配置类的@Bean解析

image

直到这里我们才算是对parser.parse(candidates);代码执行完毕了,最后我们将这块整体流程用一张图简介

image

,然后我们继续回到代码,在执行完parse方法后

image

然后做校验,重点从这里开始->this.reader.loadBeanDefinitions(configClasses);,我们一直更进去会知道

image

这里就是对我们之前存储的@Import注解类进行运行,并且会执行自定义的@Import类,到目前为止我们总算跑完了
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);这句代码

image

接来下的内容笔者打算分开描述。

文章来源微信公众号《七天0》

下一章节  invokeBeanFactoryPostProcessors(下)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值