Spring框架源码 @configuration源码深度解析
Posted Dream_it_possible!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring框架源码 @configuration源码深度解析相关的知识,希望对你有一定的参考价值。
@Configuration 注解是spring-context模块提供的一个给开发者使用的配置类注解,开发者可以通过@Configuration注解来定义配置类,也可以使用xml形式注入。
例如配置数据库配置,定义一个配置类,注入数据源DataSource, 事务管理器TransactionManager, 多数据源管理等都可以使用@Configuration 类来标记该类是一个配置类,Spring 框架会扫描并解析该类里的Bean 并注入,下面就如何解析配置类源码解析。
官方源码解释:
1. Indicates that a class declares one or more @Bean methods and may be processed by the spring container to generate bean definitions and service requests for those beans at runtime.
表示一个类声明了一个或多个被@Bean注解标记的方法, 他们应该会在运行时被Spring 容器处理并会为这些Bean产生bean definitions 和服务请求。
@Configuration
public class AppConfig
@Bean
public MyBean myBean()
// instantiate, configure and return bean ...
简单讲: @Configuration 标记的类可以用@Bean 定义很多Bean method 。
一、@Configuration 用法
1. 注解形式@Configuration
@Configuration
public class AppConfig
@Bean
public MyBean myBean()
// instantiate, configure and return bean ...
使用AnnotationConfigurationApplicationContext初始化配置类。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);
// use myBean ...
2. xml形式
使用<context:annotation-config >属性声明, xml形式需要使用ClassPathXmlApplicationContext来加载。
<beans>
<context:annotation-config/>
<bean class="com.acme.AppConfig"/>
</beans>
官方: In the example above, <context: annotation-config> is required in order to enable ConfigurationClassPostProcessor.
我们从官方解释中看到,根据配置会进入到ConfigurationClassPostProcessor,那ConfigurationClassPostProcessor 会是扫描这些配置Bean的入口。
真正调用ConfigurationClassPostProcessor是在AbstractApplication里的refresh()方法里。invokerBeanFactoryPostProcessors()能调用所有invokeBeanDefinitionRegistryPostProcessors和invokerBeanFactoryPostProcessors,即所有实现BeanFactoryPostProcessor接口和BeanDefinitionRegistryPostProcessor接口的所有实现类都会被回调。
invokeBeanFactoryPostProcessors:
invokeBeanDefitionRegistryPostProcessors:
二、ConfigurationClassPostProcessor源码解析
ConfigurationClassPostProcessor 类实现了BeanFacotryPostProcessor和BeanDefinitionRegistryPostProcessor 两个接口, 是解析@Configuration类的入口。
1. BeanFactoryPostProcessor: PostProcessBeanFactory 方法入参为ConfigurableListableBeanFactory, ConfigurableListableBeanFactory的默认实现为DefaultListableBeanFactory, 提供BeanFactory所有功能与服务。
2.BeanDefinitionRegistryPostProcessor: postProcessBeanDefinitionRegistry()方法入参为BeanDefinitionRegistry。该方法的作用主要是扫描并注册所有@Configuraiton注解标记的类下的bean。
先进入到processConfigBeanDefinitions方法,看看做了哪些事?
这里我们可以发现遍历所有的BeanDefinition, 过滤掉已经处理完的ConfigurationClass. 用isFullConfigurationClass和isLiteConfigurationClass来判断。
如果已经设置了候选人身份,那么该判断就为true, 也就不会继续进行放入到configCondidates里。
如果之前没有设置候选人身份,那么在执行CheckConfigurationClassCandidate时,会检查是否为FullConfigurationCandidate和LiteConfigurationCandidate, 接着看FullConfigurationCandidate和LiteConfigurationCandidate。
FullConfigurationCandidate和LiteConfigurationCandidate
从字面意思上理解,一个为满,一个为简的候选人,那他们真正的含义是什么呢?
FullConfigurationCandidate 直接被@Configuration注解标记的类被称为FullConfigurationCandidate。
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata)
return metadata.isAnnotated(Configuration.class.getName());
LiteConfigurationCandidate 主要是指被@Component、@ComponentScan、@Import、@ImportSource注解标记的类。
private static final Set<String> candidateIndicators = new HashSet<>(8);
static
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata)
// Do not consider an interface or an annotation...
if (metadata.isInterface())
return false;
// Any of the typical annotations found?
for (String indicator : candidateIndicators)
if (metadata.isAnnotated(indicator))
return true;
// Finally, let's look for @Bean methods...
try
return metadata.hasAnnotatedMethods(Bean.class.getName());
catch (Throwable ex)
if (logger.isDebugEnabled())
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
return false;
如果是FullConfigurationCandidate或者LiteConfigurationCandidate, 那么给该Definition打个标记表示该BeanDefinition已经候选过, 那下次扫描的时候就会跳过该BeanDefinition。
if (isFullConfigurationCandidate(metadata))
// 表示已经候选过
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
else if (isLiteConfigurationCandidate(metadata))
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
走到这里,configCandidates有一个值了,该值我配置的MybatisConfig类:
接下来就是解析候选ConfigurationClass
解析所有的候选ConfigurationClass并加载所有的Bean
1. 由ConfigurationClassParser 解析所有的candidates。
2. 由ConfigurationClassBeanDefinitionReader 来解析该configurationClass里的所有Bean。
3. 再次检查是否有新的ConfigurationClass加入到candidates集合里,如果有那么继续解析并加载所有的Bean。
源码如下:
do
//解析所有的候选Bean
parser.parse(candidates);
//对ConfigurationClass校验, 如果是Final类会报错。
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null)
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
// 加载所有的BeanDefinitions
this.reader.loadBeanDefinitions(configClasses);
// 已经解析了加入到alreadyParsed集合了
alreadyParsed.addAll(configClasses);
candidates.clear();
// 再次检查以防有新的ConfigurationClass 加入
if (registry.getBeanDefinitionCount() > candidateNames.length)
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed)
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
for (String candidateName : newCandidateNames)
if (!oldCandidateNames.contains(candidateName))
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName()))
// 补偿未添加上的Candidates
candidates.add(new BeanDefinitionHolder(bd, candidateName));
candidateNames = newCandidateNames;
while (!candidates.isEmpty());
三、@Configuration配合Profile使用
官方源码给出了一个用法例子,对于不同的开发环境可以使用@Profile注解来指定配置文件。
@Profile("development")
@Configuration
public class EmbeddedDatabaseConfig
@Bean
public DataSource dataSource()
// instantiate, configure and return embedded DataSource
@Profile("production")
@Configuration
public class ProductionDatabaseConfig
@Bean
public DataSource dataSource()
// instantiate, configure and return production DataSource
spring boot项目使用spring.profiles.active=development来指定激活的profile, 指定哪个就用哪个。
以上是关于Spring框架源码 @configuration源码深度解析的主要内容,如果未能解决你的问题,请参考以下文章
[Spring6.0源码解析]简述@Configuration注解
Spring源码解析 – @Configuration配置类及注解Bean的解析
spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
Spring框架开发底层的@Component 通用组件模式@Service 服务模式@Configuration 配置模式等注解装配技术详解