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);
    //注册BeanConfigbeanfactory.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())) {
			//处理内部类的情况
			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来完成的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐