Springboot 3.2.1 自动装配原理

本文详细介绍了SpringBoot3.2.1版本中基于约定配置和Spring框架的自动装配过程,包括Bean的定义、初始化、自动装配策略和Spring.factories文件的作用。重点讲述了ConfigurationClassPostProcessor在解析注解和加载配置类中的关键作用。

基于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初始化,实例化过程,完成自动配置启动。

<think>好的,我现在要解决用户的问题:如何在Spring Boot 3.2.1中使用HikariPoolMXBean。首先,我需要确认HikariCP在Spring Boot中的集成情况,因为Hikari是Spring Boot默认的数据库连接池,所以应该已经自动配置了。接下来,用户需要的是使用HikariPoolMXBean,这属于JMX监控的一部分。 首先,我需要回忆Spring Boot中启用JMX的配置。通常在application.properties里设置spring.jmx.enabled=true。然后,HikariCP的MXBean是否默认注册?可能需要检查Hikari的配置项,比如是否启用了registerMbeans属性。Hikari的文档中提到,需要设置registerMbeans为true才能注册MXBean。 接下来,用户可能需要一个配置示例。应该在application.properties中添加Hikari相关的配置,比如数据源URL、用户名、密码,以及Hikari特定的参数,如最大连接数、最小空闲连接等。同时,不要忘记设置spring.datasource.hikari.register-mbeans=true来启用MXBean注册。 然后,关于如何在代码中获取HikariPoolMXBean。可能需要通过JMX客户端或者Spring的Actuator端点来访问。但用户可能更倾向于编程方式获取,比如使用MBeanServer来查找对应的ObjectName。需要导入javax.management包,并编写相应的代码来获取MXBean实例,进而调用其方法监控连接池状态。 另外,要确保Spring Boot版本是3.2.1,因为不同版本的配置可能有细微差别。需要检查Spring Boot 3.2.1的官方文档,确认HikariCP的自动配置是否有所变化,以及JMX相关的配置是否有新的属性。 可能遇到的问题包括MXBean未正确注册,这时候需要检查配置是否正确,以及应用是否运行在支持JMX的环境中。此外,权限问题也可能导致无法访问MBean,需要确保安全管理器允许访问。 最后,生成相关问题时要围绕配置、监控、优化和常见问题处理,确保覆盖用户可能关心的其他方面。</think>### 如何在 Spring Boot 3.2.1 中使用 HikariPoolMXBean #### 1. 配置 HikariCP 连接池 在 Spring Boot 3.2.1 中,HikariCP 是默认的数据库连接池。需在 `application.properties` 中添加以下配置以启用 HikariPoolMXBean: ```properties # 数据源基础配置 spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=secret # Hikari 连接池配置 spring.datasource.hikari.maximum-pool-size=10 spring.datasource.hikari.minimum-idle=2 spring.datasource.hikari.idle-timeout=30000 # 启用 JMX 注册 spring.datasource.hikari.register-mbeans=true spring.jmx.enabled=true ``` #### 2. 通过 JMX 访问 HikariPoolMXBean HikariPoolMXBean 提供连接池的运行时指标(如活跃连接数、空闲连接数等)。可通过以下方式访问: ```java import javax.management.JMX; import javax.management.MBeanServer; import javax.management.ObjectName; import com.zaxxer.hikari.HikariPoolMXBean; public class HikariMonitor { public static void monitorHikari() throws Exception { MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); ObjectName objectName = new ObjectName("com.zaxxer.hikari:type=Pool (your-pool-name)"); HikariPoolMXBean mxBean = JMX.newMXBeanProxy(mBeanServer, objectName, HikariPoolMXBean.class); System.out.println("Active Connections: " + mxBean.getActiveConnections()); System.out.println("Idle Connections: " + mxBean.getIdleConnections()); } } ``` #### 3. 通过 Spring Actuator 集成(可选) 若已引入 `spring-boot-starter-actuator`,可通过 `/actuator/metrics/hikari.*` 端点获取指标: ```properties management.endpoints.web.exposure.include=metrics ``` #### 关键注意事项 - **JMX 名称匹配**:`your-pool-name` 需与 `spring.datasource.name` 定义的名称一致(默认未显式配置时为 `dataSource`)[^1] - **版本兼容性**:Spring Boot 3.2.1 使用 HikariCP 5.x,需确保依赖正确 - **安全限制**:生产环境中需检查 JMX 端口和防火墙设置 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值