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

  • 相关阅读:
    C++对象之间的赋值运算符
    WebService--导出excel并将excel发送到邮箱
    jakarta-oro-2.0.8.jar-----------JAVA FTP相关
    排序函数sort用法简介
    2013年第四届蓝桥杯全国软件大赛本科A组c++预赛 题目及参考答案
    2012年第三届蓝桥杯全国软件大赛c++预赛 题目及参考答案
    数论学习小记 其之三 Gcd与Lcm
    平面分割问题小结
    数论学习小记 其之二 同余及常用数论定理
    数论学习小记 其之一 基础数学
  • 原文地址:https://www.cnblogs.com/strongmore/p/16229546.html
Copyright © 2020-2023  润新知