Springboot 3.2.1 自动装配原理

基于Springboot3.2.1, JDK17

个人总结,如有误区,请指正。

初衷是为了能更全面地理解,增强在项目中的实际应用,以及提升后期排错能力。

创建一个demo Spring boot:https://start.spring.io/

Q1: Spring boot是什么?
是一个企业级Java应用开发轻量级框架。基于Spring强大的生态,通过约定大于配置的思路,简化开发难度(通过IOC,AOP,以及注解的方式),让开发人员更专注于对业务的实现。

Q2: 简要地说明Springboot如何运作?

  • 基于IOC容器运作流程。

    • BeanDefinition
    • BeanDefintionRegistry
    • BeanDefinitionReader
    • BeanFactoryPostProcessor
    • BeanFactory
    • ApplicationContext
    • BeanPostProcessor
    • Bean
  • 先定义bean源信息(途径:xml配置,annonation注解配置,或其他)

  • 将bean信息通过BeanDefinitionReader读取,加载成规范的BeanDefinition

  • 在真正BeanDefinitionBeanFactory拿去做初始化之前,Spring会通过BeanFactoryPostProcessor后置处理器作为扩展功能,比如:org.springframework.context.support.PropertySourcesPlaceholderConfigurer<context:property-placeholder>在xml中动态定义db参数的扩展,实现可以通过${}方式,在指定的classpth properties文件中获取。

  • BeanFactory开始拿到BeanDefinition,开始初始化:根据beanClassName, 通过反射机制实现初始化对象。这个过程涉及到一个热点问题:Spring的循环依赖:三级缓存。后续再说。

  • 在实例化之前,Spring定义了另外一个扩展:BeanPostProcessor。这一个类定义了两个方法:#postProcessBeforeInitialization & #postProcessAfterInitialization。顾名思义,会在实例化的前后对Bean对象进行扩展修饰。

  • 实例化Bean对象。会对成员变量进行populate初始值。
    在这里插入图片描述

Q3: Springboot如何实现自动装配?

  • 基于Q2的理解,可以得到两个信息,如果我想自定义做一些扩展功能。可以从PostProcessor这个后置处理器上下功夫。
  • 针对自动装配,是通过pom加入依赖,主类加注解@SpringbootApplication,以及main方法的编写实现的。
  • 此外,可以确定的是,只要确保在BeanFactory在初始化Bean对象的时候,能给到正确规范的BeanDefinition定义对象即可。不论bean源信息是如何实现的。
  • 所以,针对bean源信息的定义:注解,xml,javaconfig,抑或是自定义的json等等形式,只要确保定制专门的reader以及beanFactoryPostProcessor能够最终得到正确规范的beanDefinition。Springboot的自动装配的确也是这么做的:
  • 在代码层面上,在#refresh方法中的#finishBeanFactoryInitialization方法(beanFactory完成对Bean对象的初始化,实例化过程)之前,我可以做自动装配这个动作:(这里列举了详细过程,结尾有总结)
    • 在#main方法启动时,
      • 首先SpringApplication类会被实例化,在此期间,有两个主要的点:

        • cache会被load所有的META-INF/spring.factories下的resource
        • 设置主类primarySource在这里插入图片描述

        BTW:Springboot源码中,在给map赋值时,使用#computeIfAbsent方法(如果key对应的value为null,那么执行lambda表达式,返回的值,赋值给key对应的value值)频次极高。

	public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
        Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
        ClassLoader resourceClassLoader = classLoader != null ? classLoader : SpringFactoriesLoader.class.getClassLoader();
        // 第一次cache是个size=0的map集合
        // computeIfAbsent后,会将 resourceClassLoader::loader存进cache的集合中
        Map<String, SpringFactoriesLoader> loaders = (Map)cache.computeIfAbsent(resourceClassLoader, (key) -> {
            return new ConcurrentReferenceHashMap();
        });
		// 这里会对loader进行存值,值就是来自于META/INF/spring.factories
        return (SpringFactoriesLoader)loaders.computeIfAbsent(resourceLocation, (key) -> {
            return new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation));
        });
    }

在这里插入图片描述

  1. 执行#run方法过程:
    • 当执行到#prepareContext时,会去加载主类:从primarySource拿到主类,通过创建BeanDefintionLoader加载器,使用AnnonatedBeanDefinitionReader将主类包装成AnnotatedGenericBeanDefinition注册到beanFactoryBeanDefinitionRegistry)中(主类被加到beanDefinitionMap中)。这一步主要是为了后面执行ConfigurationClassPostProcessor(这是一个BeanFactoryPostProcessor)调用时,找到主类,完成对注解的剖析过程。

    • 随后执行#refreshContext方法:

      • 方法#invokeBeanFactoryPostProcessor,会去从BeanFactory中拿取类型为BeanDefinitionRegistryPostProcessor(这继承了BeanFactoryPostProcessor),随后获得这次的主角:ConfigurationClassPostProcessor
        • 程序会调用到ConfigurationClassPostProcessor的#processConfigBeanDefinitions方法:
          • 先从beanDefinitionNames中拿到主类,且包装成BeanDefinitionHolder
          • 随后,会定义ConfigurationClassParser类,在方法#processConfigurationClass方法中,递归处理Configuration class及其super class:
            • 在方法#processImports中的getImports, 通过主类注解@SpringBootApplication,递归调用,找到包括父注解中,包含@Import注解上的class类(@EnableAutoConfiguration注解上的AutoConfigurationImportSelector类,@AutoConfigurationPackage注解上的AutoConfigurationPackages.Registrar类)。
        • 随后,再通过SelectorHandler的#processGroupImports方法,调用到AutoConfigurationImportSelector的#process – #getImports – #getAutoConfigurationEntry – #getCandidateConfigurations获得在路径META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports下所有类对象。
        • 拿到所有**AutoConfiguration类后,跟主类同样地操作,去遍历处理各个类的注解信息,找到@Import下的类,并对其进行解析。
        • 查找完之后,我本地最终剩余54个config class。
        • 通过创建ConfigurationClassBeanDefinitionReader,#loadBeanDefinitions方法读取加载这些Config class。
        • 最终会将加载好的config class以及config class下的加有@Bean的method方法以形式<beanName, BeanDefinition>的数据方式放在BeanFactory下的beanDefinitionMap中。
        • 然后,在这个时机,才去真正遍历调用BeanFactoryPostProcessor的post方法。
        • invokeBeanFactoryPostProcessors方法调用完成后,查看到beanFactoryBeanDefinitionNames属性,会发现,之前加载的config class以及class下的method bean都在这个列表里面。
        • 在实例化Bean时,config class会被加载,完成对Springboot的启动配置。

总而言之,springboot自动装配是通过ConfigurationClassPostProcessor 主要完成了对注解的解析(ConfigurationClassParser),对所有config class加载读取(ConfigurationClassBeanDefinitionReader)两项主要工作。

  1. 解析工作:通过遍历主类注解,查找出@Import的导入类,通过该类从META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports下查找到所有的config class,也会对查找的config class进行同样的遍历查找,找出所有的配置类。

  2. 加载读取工作:会对找出的所有配置类,配置类中的Method Bean, 配置类的importedResources以及配置类的ImportBeanDefinitionRegistrars进行读取,放置到BeanFactoryBeanDefinitionMap中,等待随后的bean初始化,实例化过程,完成自动配置启动。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值