ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring源码分析之@Configuration注解的处理

2022-05-13 19:02:14  阅读:147  来源: 互联网

标签:configClass Spring 配置 Bean 源码 注解 new Configuration sourceClass


前言

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

标签:configClass,Spring,配置,Bean,源码,注解,new,Configuration,sourceClass
来源: https://www.cnblogs.com/strongmore/p/16229546.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有