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为配置类的逻辑为

  1. 判断Class是否包含@Configuration注解,如果包含,为配置类
  2. 如果没有,@Component,@ComponentScan,@Import,@ImportResource,查看是否包含此4个注解之一,如果包含,为配置类
  3. 如果没有,判断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注解,此注解配置的类型可以有三种:

  1. ImportSelector接口类型,可以看做一个导入选择器,返回多个要导入的Class类型,如SpringBoot自动装配的实现AutoConfigurationImportSelector。
  2. ImportBeanDefinitionRegistrar,可以看做一个注册器,Spring提供的一个钩子,可以让我们向BeanDefinitionRegistry中添加自定义的BeanDefinition,
    如开启AOP的AspectJAutoProxyRegistrar。
  3. 其他类型的配置类

接下来继续分析配置类的加载,进入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框架的核心有两个:

  1. 注册BeanDefinition到容器中
    从各种渠道注册,如XML配置文件,@Component注解,@Bean注解,手动创建BeanDefinition注册,各种扩展类如ImportBeanDefinitionRegistrar的注册。
  2. 根据BeanDefinition创建Bean对象
    创建过程中,可能会创建代理对象,这就是AOP的功能。

Spring很多附加的功能都是通过帮我们自动注册了很多BeanDefinition来完成的。

posted @ 2022-05-13 18:48  strongmore  阅读(236)  评论(0编辑  收藏  举报