Spring源码分析之@Configuration注解的处理
前言
Spring从3.0开始支持JavaConfig配置,具体来说就是可以完全通过注解来开启Bean扫描,声明Bean,导入properties文件等。
主要有以下注解:
@Configuration: 标识此Bean是一个配置类,接下来开始解析此类
@ComponentScan: 开启注解扫描,默认扫描@Component注解
@Import: 导入其他配置类
@ImportResource: 导入其他XML配置文件
@Bean: 在方法上使用,声明此方法为一个Bean
简单使用
import java.util.ArrayList;
import java.util.LinkedList;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class TestConfiguration {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(BeanConfig.class);
//注册BeanConfig类
beanFactory.registerBeanDefinition("beanConfig", builder.getBeanDefinition());
//处理@Configuration注解
ConfigurationClassPostProcessor configurationClassPostProcessor = new ConfigurationClassPostProcessor();
configurationClassPostProcessor.postProcessBeanDefinitionRegistry(beanFactory);
configurationClassPostProcessor.postProcessBeanFactory(beanFactory);
System.out.println(beanFactory.getBean("beanConfig").getClass());
System.out.println(beanFactory.getBean("myArrayList").getClass());
System.out.println(beanFactory.getBean(LinkedList.class).getClass());
}
@Configuration(proxyBeanMethods = true)
@ComponentScan
@Import({MyBeanRegistrar.class, MySelector.class})
public static class BeanConfig {
}
public static class MyBeanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(ArrayList.class);
registry.registerBeanDefinition("myArrayList", builder.getBeanDefinition());
}
}
public static class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{LinkedList.class.getName()};
}
}
}
这里为了更好的分析原理,没有使用更加强大的ApplicationContext,核心类为ConfigurationClassPostProcessor,
这是一个BeanDefinitionRegistryPostProcessor(BeanDefinitionRegistry后置处理器,可以让对BeanDefinitionRegistry进行扩展处理,如添加自定义的BeanDefinition),
也是一个BeanFactoryPostProcessor(BeanFactory后置处理器,可以让我们扩展BeanFactory)。
ConfigurationClassPostProcessor会判断Bean是否为一个配置类,如果是,就解析此类,具体就是解析@ComponentScan,@Import等注解。
如果我们使用支持JavaConfig的ApplicationContext,它会通过AnnotationConfigUtils的registerAnnotationConfigProcessors()方法来自动添加ConfigurationClassPostProcessor类。
ApplicationContext会在refresh()方法执行过程中处理ConfigurationClassPostProcessor的后置方法,
关于ApplicationContext,可以查看Spring源码分析之ApplicationContext 。
源码分析
在Spring源码分析之ApplicationContext 的基础上,我们可以知道,在refresh()方法的步骤invokeBeanFactoryPostProcessors()中,
会执行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法,
然后再执行BeanFactoryPostProcessor的postProcessBeanFactory()方法,所以我们先分析ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法。
postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//处理配置
processConfigBeanDefinitions(registry);
}
继续跟进去
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
//过滤出所有Bean中为配置类的Bean,下面会说判断的条件
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//没有配置类
if (configCandidates.isEmpty()) {
return;
}
//按照优先级排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
//从容器中查找Bean名称生成器
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
//创建environment
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
//配置类解析器
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
//真正开始解析
parser.parse(candidates);
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());
}
//从解析好的配置类中加载BeanDefinition,注册到容器中
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
//如果解析配置类的过程中,又导入了其他配置类,继续解析
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.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
//将ImportRegistry注册为一个Bean,用来支持ImportAware钩子回调
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
判断一个Bean为配置类的逻辑为
- 判断Class是否包含@Configuration注解,如果包含,为配置类
- 如果没有,@Component,@ComponentScan,@Import,@ImportResource,查看是否包含此4个注解之一,如果包含,为配置类
- 如果没有,判断Class是否有方法包含@Bean注解
上述逻辑汇总,核心地方有两个,一个是解析配置类,一个是加载配置类,先看解析,进入ConfigurationClassParser解析器
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
//根据不同的BeanDefinition类型调用不同的方法,最后解析时会统一处理
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
}
//这个处理也是很重要的,延迟导入,SpringBoot提供的AutoConfigurationImportSelector就是一个延迟加载的导入选择器,
//它会在我们我们自己的配置类加载之后再加载,相当于低优先级,
//因为在处理OnMissingBeanCondition等注解时需要依赖前面的配置类来判断某个Bean是否已经在容器中存在
this.deferredImportSelectorHandler.process();
}
不同的BeanDefinition类型,都会统一创建一个ConfigurationClass来处理,继续跟进去
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
//处理@Conditional注解,根据条件判断该配置类是否需要被加载
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
//处理已经解析过的情况
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
return;
}
else {
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
//递归处理配置类及父类
SourceClass sourceClass = asSourceClass(configClass);
do {
//核心,解析配置类
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
开始真正的处理解析配置类
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
//处理成员类的情况(就是内部类和静态内部类,通过 asm 等字节码库解析得到)
processMemberClasses(configClass, sourceClass);
}
//解析@PropertySource注解,用来处理properties文件,添加到environment中
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
}
//解析@ComponentScan注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
//内部使用ClassPathBeanDefinitionScanner扫描器,默认扫描@Component注解,注意,扫描完成已经将BeanDefinition注册到容器中了
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
//如果扫描到的BeanDefinition也包含配置类,递归解析配置类
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
//判断是否为配置类
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//解析@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
//解析@ImportResource注解,可以导入XML配置文件
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
//解析包含@Bean注解的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
//处理接口相关,不用管
processInterfaces(configClass, sourceClass);
//处理父类
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
//没有父类,处理结束
return null;
}
关于解析@Import注解,此注解配置的类型可以有三种:
- ImportSelector接口类型,可以看做一个导入选择器,返回多个要导入的Class类型,如SpringBoot自动装配的实现AutoConfigurationImportSelector。
- ImportBeanDefinitionRegistrar,可以看做一个注册器,Spring提供的一个钩子,可以让我们向BeanDefinitionRegistry中添加自定义的BeanDefinition,
如开启AOP的AspectJAutoProxyRegistrar。 - 其他类型的配置类
接下来继续分析配置类的加载,进入ConfigurationClassBeanDefinitionReader的loadBeanDefinitions()方法
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
//使用一个支持追踪的条件解析器来判断配置类是否可以加载,如果A配置类是被B配置类通过@Import注解引入的,B配置类不加载,那么A配置类也不能被加载
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
//依次加载每一个配置类
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
继续
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
//如果不能被加载,从容器中删除
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
//如果此配置类是被导入的,注册此配置类到容器中
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//注册所有包含@Bean注解的方法到容器中,这种Bean通过工厂方法来实例化
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//使用XmlBeanDefinitionReader从XML配置文件中加载所有Bean,其实还支持groovy类型的配置文件,用的不多,就先不管了
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//从注册器加载,依次调用所有ImportBeanDefinitionRegistrar的registerBeanDefinitions()方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
至此Spring已经将所有的BeanDefinition都注册到容器中了。
postProcessBeanFactory
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//使用CGLIB对配置类创建动态代理
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
通过@Configuration(proxyBeanMethods = true)
来标记开启代理,默认就是true,主要是为了处理下面这种情况
@Configuration(proxyBeanMethods = true)
public static class BeanConfig {
@Bean("myHashSet")
public Set<String> myHashSet() {
return new HashSet<>();
}
@Bean("myArrayList")
public List<String> myArrayList() {
return new ArrayList<>(myHashSet());
}
@Bean("myLinkedList")
public List<String> myLinkedList() {
return new LinkedList<>(myHashSet());
}
}
在配置类中声明了3个Bean,按理来说只会创建一个名称为myHashSet的Bean,但myArrayList()和myLinkedList方法内部都调用了myHashSet()方法,不能创建两个myHashSet的Bean,
这就是因为Spring对配置类创建了动态代理对象,当调用myHashSet()方法时,会根据方法找到对应的Bean名称,从容器中查询出对应的Bean对象。
分析总结
Spring处理配置主要有以下几个类:
- ConfigurationClassPostProcessor: 框架类,使用下面的几个类来完成解析,加载
- ConfigurationClassParser: 解析所有配置类
- ConfigurationClassBeanDefinitionReader: 加载所有配置类
- ConfigurationClassEnhancer: 根据需要对配置类创建代理
Spring框架的核心有两个:
- 注册BeanDefinition到容器中
从各种渠道注册,如XML配置文件,@Component注解,@Bean注解,手动创建BeanDefinition注册,各种扩展类如ImportBeanDefinitionRegistrar的注册。 - 根据BeanDefinition创建Bean对象
创建过程中,可能会创建代理对象,这就是AOP的功能。
Spring很多附加的功能都是通过帮我们自动注册了很多BeanDefinition来完成的。