Open xtyuns opened 1 year ago
@Configuration
@Configuration
注解继承了 @Component
,因此它的本质也是用于标注一个 Spring Bean,不同的是 @Configuration
常用于那些包含 @Bean
方法的类上(我们通常称为配置类),此时 Spirng 会使用 CGLIB 为这些配置类生成动态代理对象,并且在代理类中将标注了 @Bean
的方法的返回值进行缓存(只有第一次调用方法时会产生新的 Bean 并装入 IOC 容器, 后续的其他调用则直接返回 IOC 中对应的 Bean),以使得多次调用该方法时始终返回同一个对象,这被称为 FullConfiguration,也可以为配置类设置 @Configuration(proxyBeanMethods = false)
以禁用该特性,@Configuration(proxyBeanMethods = false)
等同于 @Component
,这被称为 LiteConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class Beans {
@Bean
fun defaultMap() : Map<String, Any> {
val defaultMap = mapOf<String, Any>("now" to System.currentTimeMillis())
println("create hashMap: ${defaultMap.hashCode()}")
return defaultMap
}
@Bean
fun defaultMapSize(): Int {
val defaultMap = defaultMap()
println("use defaultMap: ${defaultMap.hashCode()}")
return defaultMap.size
}
}
当 Spring 启动时打印内容为
create hashMap: 2013879770
use defaultMap: 2013879770
但是将 @Configuration
修改为 @Configuration(proxyBeanMethods = false)
或 @Component
后,打印内容为
create hashMap: 2016753065
create hashMap: 2016753064
use defaultMap: 2016753064
在 IDEA 中会分别提示:
@Bean
is called directly in a @Configuration
where proxyBeanMethods set to false. Set proxyBeanMethods to true or use dependency injection.@Bean
is called directly. Use dependency injection instead.因此当配置类中不存在 Bean Mehtod 的直接调用时,推荐使用 LiteConfiguration 以消除 CGLIB 带来的性能影响
相关源码:标注 FullConfiguration,设置 BeanDefinition 的 BeanClass 为动态代理类
Spring 中常见的 ApplicationContext 类型有
前提条件:在 SpringBoot 的启动类上添加 @SpringBootApplication
注解
一、在 createApplicationContext 阶段会创建对应的 Context,并且为属性 reader 实例化一个类型为 AnnotatedBeanDefinitionReader 的对象,而该属性在实例化的过程中通过调用 AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry) 向 Context.beanFactory 中注册了一个类型为 ConfigurationClassPostProcessor 的 BeanDefinition
二、另一方面,在 prepareContext 阶段会将 SpringApplication 中的所有 source(一般都是作为参数传入 SpringApplication.run 的 primarySources) 封装为 BeanDefinition 注册到 Context.beanFactory 中
至此,SpringBoot 自动装配的前置条件都已经准备完毕,接下来的 refreshContext 将执行自动装配功能
三、在 refreshContext 阶段中 invokeBeanFactoryPostProcessors 阶段主要是按照以下顺序依次调用了 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 和 BeanFactoryPostProcessor#postProcessBeanFactory 这两个接口中定义的方法
其中在第 2 步的调用中会触发阶段一中注册的 BeanDefinition, 因此会调用 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
四、在 ConfigurationClassPostProcessor#processConfigBeanDefinitions 中会过滤出 BeanDefinition 中的配置类(FullConfiguration 和 LiteConfiguration),并在排序后进行解析,而这之中的配置类就包含步骤二中由 primarySources 封装的 BeanDefinition(因为启动类上的 @SpringBootApplication
注解继承了 @Configuration
)
五、在针对启动类的解析过程 中
@SpringBootApplication
的 @ComponentScan
注解,扫描 basePackages 下的所有 Component 并转换为 BeanDefinition 进行递归解析@EnableAutoConfiguration
中的 @Import
注解六、在针对启动类的解析过程结束后,将对步骤五 @EnableAutoConfiguration
注解中导入的 AutoConfigurationImportSelector 进行处理,大致的流程就是加载 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中的配置类,过滤并进行导入处理,导入处理的过程和解析配置类的流程类似,采用递归处理 @Import
和配置类的解析,从而将所有相关的 Bean 都封装为 BeanDefinition 注册到 Context.beanFactory 中
七、接下来,在 refreshContext 阶段中的 registerBeanPostProcessors 会采用类似步骤三的流程将类型为 BeanPostProcessor 的 BeanDefinition 实例化为对应的 Bean 并注册到 Context.beanFactory.beanPostProcessors 中
八、在 refreshContext 阶段中的 finishBeanFactoryInitialization 将会通过调用 AbstractBeanFactory#getBean(java.lang.String) 方法来触发所有非懒加载 Bean 的实例化过程
至此,refreshContext 阶段及自动装配流程结束
@Autowired
Spring Bean 属性注入是在对象创建之后的属性填充阶段完成的。
在这个阶段将通过 BeanFactory 中 AutowiredAnnotationBeanPostProcessor#postProcessProperties 完成对 @Autowired
注解的属性注入。
所注入的属性值的是通过 DefaultListableBeanFactory#doResolveDependency 获取的,在获取备选对象的过程中主要有以下两个问题:
在 doResolveDependency 中会先通过 findAutowireCandidates(byType) 过滤出应用中所有的备选注入对象(如 @Qualifier
过滤)并返回一个 Map 集合。
对于第一种情况,则会进一步判断该注入属性是否为必须属性(@Autoired(required = true)
,默认值),如果为 required 则抛出异常终止应用启动,否则返回 null 进行属性注入。
而对于应用中存在多个备选对象的情况,则通过 determineAutowireCandidate 从上述的 map 中获取一个合适的 beanName 然后从 map 中获取对应的对象并返回(byName),获取 beanName 的具体的规则为:
@Primary
标注的对象名称@jakarta.annotation.Priority
注解,使用其最高优先级的对象名称最后,使用反射将选取的注入对象设置到目标 Bean 中。
todo
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
在框架设计中, SpringBoot 大量使用了类 SPI 机制和事件广播