Spring源码解读-@Configuration配置等注解源码分析


一 介绍

在spring的ioc中,对于bean的声明常见的有xml文件解析,BeanDefinitionRegistry注入,@Configuration注解的方式,在目前微服务大行其道,spring boot和spring cloud作为主流微服务架构开发的首选的时候,我们更多的是提倡基于注解形式的开发。
并且 spring boot社区推荐使用基于JavaConfig的配置形式来取代基于xml的配置形式

一 源码解读

1,使用

	@Test
	public void test() {
		/**
		 * 将ConfigurationClassPostProcessor的BeanFactoryPostProcessor注入到容器中,进行解析
		 */
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfiguration.class);

		//使用@Bean标签加入的
		TestBean testBean = (TestBean) applicationContext.getBean("testBean");
		System.out.println("testBean=="+testBean);

		//使用@Component注解注入的bean
		TestComponent testComponent = (TestComponent)applicationContext.getBean("testComponent");
		testComponent.testComponent();

		//@Import Selector
		Selector selector = (Selector)applicationContext.getBean("com.zcs.configuration.Selector");
		selector.print();

		//@Import normal class
//		NormalImportClass normalImportClass = (NormalImportClass) applicationContext.getBean("com.zcs.configuration.NormalImportClass");
//		normalImportClass.print();
	}

首先我们使用AnnotationConfigApplicationContext spring的扩展类实例化一个ApplicationContext,AnnotationConfigApplicationContext 接收一个字节码类TestConfigurationTestConfiguration类的具体内容如下:

/**
 * @author: zhoucg
 * @date: 2019-05-09
 */
@Configuration
@ComponentScan(basePackages = "com.zcs.configuration")
@ImportResource(value = {"classpath:/spring/spring-context.xml"})
@Import(value = {AutoConfigurationSelector.class,NormalImportClass.class})
public class TestConfiguration {

	@Bean
	public TestBean testBean(){
		return new TestBean();
	}


}

这里面定义了一些常见的注解类,@Configuration@ComponentScan@ImportResource,@Import,在下面的源码分析中,我们会一一分析这些注解类的作用,同时我们还会在源码中分析关于 @Conditional 等注解的源码解析
话不多说,首先进入到AnnotationConfigApplicationContext 类中大致的看一下提供了哪些公共的方法
在这里插入图片描述
主要的方式就是提供了一些无参和含有注解的Class类型的构造器,同时提供了设置环境变量的方法,进入到AnnotationConfigApplicationContext(Class<?>… annotatedClasses) 方法

	/**
	 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
	 * from the given annotated classes and automatically refreshing the context.
	 * @param annotatedClasses one or more annotated classes,
	 * e.g. {@link Configuration @Configuration} classes
	 */
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		//初始化bean的读取器和扫描器
		this();
		//注册bean配置文件
		register(annotatedClasses);
		refresh();
	}

主要的作用就是首先初始化bean的读取器和扫描器,注册注解类annotatedClasses,执行refresh()方法(这个方法就是执行 AbstractApplicationContext 中的refresh()方法)

	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

实例化AnnotatedBeanDefinitionReader readerClassPathBeanDefinitionScanner scanner,进入到AnnotatedBeanDefinitionReader 构造函数,最后会进入到AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) 构造函数中


	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

然后再进入到AnnotationConfigUtils 类的registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)中,重点来了

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		if (beanFactory != null) {
			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
				beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
			}
			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
			}
		}

		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(4);

		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
		if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition();
			try {
				def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
						AnnotationConfigUtils.class.getClassLoader()));
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
			}
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
		}
		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}

		return beanDefs;
	}

在上面的源码中,我们实际上会发现,该方法主要是向Spirng ioc容器中注入了这几个类
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
EventListenerMethodProcessor
DefaultEventListenerFactory
其中,我们最需要关注的两个类就是 ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessor ,其中 ConfigurationClassPostProcessor的作用就是用于引导处理@Configuration注解的类

源码注释:
{@link BeanFactoryPostProcessor} used for bootstrapping processing of  {@link Configuration @Configuration} classes.

2 ConfigurationClassPostProcessor解析
在这里插入图片描述
首先ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor ,并且实现了BeanDefinitionRegistryPostProcessor ,它是一个具有向容器注入bean的功能,因此在refresh()中的invokeBeanFactoryPostProcessors(beanFactory);方法中,是会优先执行BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法,经过前期的处理,最后是会进入到ConfigurationClassPostProcessor类中的 processConfigBeanDefinitions(BeanDefinitionRegistry registry) 方法中

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
			return;
		}

		// Sort by previously determined @Order value, if applicable
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// Detect any custom bean name generation strategy supplied through the enclosing application context
		SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

		if (this.environment == null) {
			this.environment = new StandardEnvironment();
		}

		// Parse each @Configuration class
		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
			// 读取模型并根据其内容创建bean定义
			/**
			 * 4、遍历configCandidates,使用委托类ConfigurationClassBeanDefinitionReader注册解析好的BeanDefinition
			 */
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			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());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}

最终,我们进入到了对于@Configuration核心的解析过程,(其实我们在看spring的源码中,我们会发现spring对于核心方法的实现内部是穿插了大量细节的实现,但是这些细节的实现,spring都是通过委托其他类的形式进行实现,这个大大的提高了spring的扩展性延申性)
在这个方法中,主要是做了几件事,
1,遍历目前的spring容器中,所有已解析的beandefintions,从beandefintions中找到含有@Configuration注解的类加入到configCandidates中,(实际上注解含有@Component,@ComponentScan,@Import,@ImportResource,@Configuration都可以)
2,根据@Order对configCandidates队列进行排序,
3,遍历不为空的configCandidates,然后委托ConfigurationClassParse进行解析配置,包含了@PropertySource注解解析,@ComponentScan注解解析,@Import 注解解析,@Bean注解解析,
4,遍历configCandidates,使用委托类ConfigurationClassBeanDefinitionReader注册解析好的beanDefinitioin

下面,我们会就主要的流程中的主要的问题,进行分析,spring的解析过程
对于上面的第一点,解析过程是委托给了ConfigurationClassUtils 类的checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) 方法来遍历当前spring容器中,所有已解析beandefinitions中符合条件的类加入到configCandidates中,

那么,这个时候是有这么一个问题,spring ioc中当前存在哪些已解析的beandefinions呢,
这个问题,其实很好理解,在解析之前,spring其实是只存在之前通过AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)注入的几个配置类,以及通过 this.reader.register(annotatedClasses);注入到ioc容器的TestConfiguration
在这里插入图片描述
对于符合条件的beandefintions的判断,spring是通过ConfigurationClassUtilsisFullConfigurationCandidateisLiteConfigurationCandidate方法进行判断
在这里插入图片描述
对于上面的第三点,spring是首先委托ConfigurationClassParser 进行解析,进入到parse(Set configCandidates)方法,最后是会交由doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)进行解析

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		// Recursively process any member (nested) classes first
		processMemberClasses(configClass, sourceClass);

		/**
		 * https://www.cnblogs.com/whx7762/p/7885735.html
		 * 通过@PropertySource注解将properties配置文件中的值存储到Spring的Environment中,Environment接口提供方法去读取配置文件的值
		 *
		 */
		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}
		/**
		 * @ComponentScan 解析,ComponentScanAnnotationParser解析器
		 * 首先是解析@Conditional注解(判断该@ComponentScan注解是否继续解析)
		 *
		 */
		// Process any @ComponentScan annotations
		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) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						/**
						 * 继续检测@ComponentScan解析出来的bean是否为ConfigurationClass类型,然后再进行解析
						 */
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}
		/**
		 * 测试
		 */
		BeanDefinitionRegistry beanDefinitionRegistry = this.registry;
		String[] beanDefinitions = beanDefinitionRegistry.getBeanDefinitionNames();
		for(String beanDefinition : beanDefinitions) {
			logger.debug("[zhoucg Test]   current haven load BeanDefinition:["+beanDefinition+"]");
		}

		/**
		 * 解析@Import,
		 */
		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
		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);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		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();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

在讲解这个之前,spring关于注解**@Conditional的解析是在processConfigurationClass(ConfigurationClass configClass)方法中(这个就先不研究了)
在这里插入图片描述
到了这,我们会发现,spring是会优先解析@ComponentScan注解,然后判断注解中含有@Component,@Service,或者自定义的注解加入到ioc中(这一步也是极其繁琐,可以进入到ComponentScanAnnotationParser的parse()方法中研究),然后将解析到的beandefintions再进行parser解析,查看对应的配置类是否同样含有@Configruation,@Import等注解,
然后,执行@Import和@ImportResource注解,这两个解析操作时会涉及到
ImportSelector** (这个就是spring boot自动装配的实现原理)的解析
然后是执行@Bean的解析

如果你坚持看到这,也许,你会有这么一个疑问,在ConfigurationClassPostProcessor解析的时候,会认为带@Component注解的类为Configuration的类,进行解析,但是在使用委托类ConfigurationClassParse进行解析的时候并不包含@Component的解析,这是为什么?
答案就是使用@Component注解类时,ioc会认为他是一个组件,当使用基于注释的配置和类路径扫描时,这些类被认为是自动检测的候选类。可以查看{@link org.springframework.stereotype.Component @Component} 代码注解

一 源码地址

spring关于@Configuration注解类的实现,可以说为后来的spring boot和spring cloud的开发提供了很完美的扩展。

源码github地址源码说明
https://github.com/zcswl7961/spring/blob/master/spring-mytest/src/test/java/com/zcs/config/SpringApplication.java基于spring5
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值