SpringBoot源码学习——BeanDefinition注册以及@Configuration的解析

介绍

在理解Bean的注册以及配置类是如何解析之前,有几个贯穿容器整个IOC特性的工具类需要先了解一下

1.BeanDefinition——spring bean的建模对象

那么什么是spring bean的建模对象呢?一言概之就是把一个bean实例化出来的模型对象?有人会问把一个bean实例化出来有Class就行了啊,Class也就是我们通常说的类对象,就是一个普通对象的建模对象,那么为什么spring不能用Class来建立bean呢?很简单,因为Class无法完成bean的抽象,比如bean的作用域,bean的注入模型,bean是否是懒加载等等信息,Class是无法抽象出来的,故而需要一个BeanDefinition类来抽象这些信息,以便于spring能够完美的实例化一个bean

上述文字可以简单理解spring当中的BeanDefinition就是java当中的Class
Class可以用来描述一个类的属性和方法等等其他信息
BeanDefintion可以描述springbean当中的scope、lazy,以及属性和方法等等其他信息

2.BeanDefinitionHolder

该类本质上还是一个BeanDefinition,然而它会比我们的BeanDefinition多一个beanName和alias属性,因为一个类注册到容器中首先这个类会被解析成BeanDefinition,然后会被put到一个beanDefinitionMap中,其中的key就是beanName,也就是在容器中对这个类的唯一标识名。

注意,BeanDefinition本身并不直接携带beanName,通常它的beanName由一个内置的BeanName生成器产生,所以如果我们需要依赖于beanName操作,那么我们需要在获取BeanDefinition时将其封装成BeanDefinitionHolder对象

public class BeanDefinitionHolder implements BeanMetadataElement {

	private final BeanDefinition beanDefinition;

	private final String beanName;

	@Nullable
	private final String[] aliases;
	...
	}

3.BeanDefinitionReaderUtils

该类只有一个作用,那就是将一个BeanDefinitionHolder实例注册到容器中,也就是put到容器的beanDefinitionMap中

public abstract class BeanDefinitionReaderUtils {
...
public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}
...

4.AnnotatedBeanDefinitionReader

根据官方文档来看,这个类属于 ClassPathBeanDefinitionScanner的替代品, ClassPathBeanDefinitionScanner可以对目标路径下所有类进行批量加载注册,而AnnotatedBeanDefinitionReader则可以对一些单独的特别的被注解的类进行加载注册(某些类是动态生成的或是第三方交互时获得的,因此在扫描期间它们并不存在,需要手动进行读取)

该类依赖于前面提到的BeanDefinitionReaderUtils,同时依赖于ConditionEvaluator,后者是一个判断目标注解类是否符合自身标注的Conditional条件来决定是否跳过该类的注册和加载

public class AnnotatedBeanDefinitionReader {
	private final BeanDefinitionRegistry registry;

	private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;

	private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

	private ConditionEvaluator conditionEvaluator;
	...
	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {

		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		//判断该类是否符合注解条件
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(supplier);
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		//生成beanName
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
		...
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		//注册bean
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
}

5.BeanDefinitionLoader

前面提到的类都是关于类的注册,但是在注册之前,首先需要获取到类信息。因此该类的作用就是通过不同的数据源获取BeanDefinition实例,然后交由前面的AnnotatedBeanDefinitionReader完成注册

通过下面直接管关联的成员来看,BeanDefinitionLoader可以对注解类进行读取、对xml文件进行读取,对Resouce对象进行读取,同时还能够通过ClassPathBeanDefinitionScanner进行扫描读取,然后完成注册

class BeanDefinitionLoader {

	private final Object[] sources;

	private final AnnotatedBeanDefinitionReader annotatedReader;

	private final XmlBeanDefinitionReader xmlReader;

	private BeanDefinitionReader groovyReader;

	private final ClassPathBeanDefinitionScanner scanner;

	private ResourceLoader resourceLoader;
	...
	private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		if (source instanceof Class<?>) {
			return load((Class<?>) source);
		}
		if (source instanceof Resource) {
			return load((Resource) source);
		}
		if (source instanceof Package) {
			return load((Package) source);
		}
		if (source instanceof CharSequence) {
			return load((CharSequence) source);
		}
		throw new IllegalArgumentException("Invalid source type " + source.getClass());
	}
	private int load(Class<?> source) {
		if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
			// Any GroovyLoaders added in beans{} DSL can contribute beans here
			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
			load(loader);
		}
		if (isEligible(source)) {
			this.annotatedReader.register(source);
			return 1;
		}
		return 0;
	}
	....
}

6. ConfigurationClassParser

通过前面的几个类我们可以将配置源转化成BeanDefinition对象并完成注册,但这个时候,这些配置类中还隐藏着许多的注解类需要进行进一步的解析

ConfigurationClassParser是用来处理配置类的类,它将配置类中的各个注解成分解析出来,用一个ConfigurationClass封装,如果说BeanDefinition是对容器中的普通bean类进行的封装,那么ConfigurationClass就是专门对配置类的信息封装,通过这个类我们可以得到配置类中的所有标注了@Bean的方法集合以及其他实现了BeanDefinitionReader和ImportBeanDefinitionRegistrar等的类集合。这样我们就能够很容易的完成bean的注册

class ConfigurationClassParser {
	...
	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		//处理标注了Component的类
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			processMemberClasses(configClass, sourceClass, filter);
		}

		// 处理@PropertySource注解
		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注解
		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)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// 处理@Import注解
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// 处理@ImportResource注解
		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));
		}

		// 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;
	}
	...
	}

7.ConfigurationClassBeanDefinitionReader

该类可以对解析好的ConfigurationClass对象进行处理,将其中需要注册到容器中的Bean注册到容器中

class ConfigurationClassBeanDefinitionReader {
	...
	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);
		}
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}
	...
}

8.ConfigurationClassPostProcessor

ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor,因此会在工厂后置处理器调用时执行,该类主要依赖于ConfigurationClassParser和ConfigurationClassBeanDefinitionReader,前者完成配置解析,后置完成Bean的注册

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
		PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
	...
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		...
		for (String beanName : candidateNames) {
			...
		// 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);
			...
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);
			...
			}
		while (!candidates.isEmpty());
	...
}

配置类注册解析的流程

先附带一张流程图
在这里插入图片描述

(1) 在实例化容器时容器同时实例化了AnnotationBeanDefinitionReader和ClassPathBeanDefinitionScanner

public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext{
...
public AnnotationConfigServletWebServerApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
	...
}

(2) 在构造AnnotatedBeanDefinitionReader时,该类又通过AnnotationConfigUtils向容器中注册了一个ConfigurationClassPostProcessor工厂后置处理器

public class AnnotatedBeanDefinitionReader {
...
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		...
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}
	...
}
public abstract class AnnotationConfigUtils {
	...
	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
		...
		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));
		}
		...
	}
	...
}

(3)完成容器实例化后,在SpringApplication的prepareContext方法中,SpringApplication将我们在一开始传入的配置类也就是@SpringBootApplication注解类注册到容器中

public class SpringApplication {
...
	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		...
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}
...
}

(4)容器准备完成后,会在refresh方法中调用工厂后置处理器对容器完成初始化,其中ConfigurationClassPostProcessor在调用时,会对容器的配置类进行解析,并将解析出的Bean对象著猜测到容器中
源码在上面ConfigurationClassPostProcessor的说明中

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

原来是肖某人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值