BeanDefinition的获取
Owner: Zetong Wang
示例程序:
@ComponentScan("org.example.*")
@Import({ClassF.class})
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
}
}
将会被扫描的Bean
ClassA
ClassB
ClassC
ClassD
ClassE
ClassF
现在已经知道bean的扫描获取功能是由一个工厂级别的后处理器ConfigurationClassPostProcessor 完成的,因此主要就是解读这个类的一些方法是如何完成bean的扫描的。
对象的获取即是获取将要被Spring管理的Class对象,只有知道了这些Class对象后续才可以进行实例化操作,在AnnotationConfigApplicationContext场景中我们都知道需要传入一个扫描类一般这个类上有着@ComponentScan 这个注解,因此为了知道那些类需要被注入到Spring中我们就先得把这个扫描类给放入容器中。这也就是AnnotationConfigApplicationContext 构造函数中的register() 的作用
@Override
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
.tag("classes", () -> Arrays.toString(componentClasses));
**this.reader.register(componentClasses); *1**
registerComponentClass.end();
}
1 处我们可以看到该扫描类会被一个reader进行读取,这个reader的作用就是将扫描类封装成一个BeanDefinitoin然后,使用register将其注册到我们熟知的beanDefinitionMap中*。**
还有一点就是之前所提到的ConfigurationClassPostProcessor 是在初始化AnnotationConfigApplicationContext 中的reader 属性时注册的。
这样有扫描的基类,之后就可以知道应该去哪些包路径下面去寻找被注册的Bean。
接下来到了更加熟知refresh()中,在这个方法中我们就可以完成Bean注册以及实例化和初始化的一个完整的生命周期,在整个生命周期中着重看一下ConfigurationClassPostProcessor是在何时起到的作用。
invokeBeanFactoryPostProcessors(beanFactory);
之前看过ConfigurationClassPostProcessor 是一个BeanFactoryPostProcessor的子类实现,因此它的调用也是在这个方法中完成
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
最重要的就是这里
**PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());*1**
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
*1处Spring使用一个工具类来代理BeanPostProcessor的创建以及调用,由于在Spring中将BeanFactoryPostProcessor分为了两类 ,一种是它本身一种是他的子类BeanDefinitionRegistryPostProcessor,在进行调用的时候子类的优先级要比其本身的要高一点,而ConfigurationClassPostProcessor就是继承的子类:
节选自:PostProcessorRegistrationDelegate的104—113行
// 获取ConfigurationClassPostProcessor的bean名称,叫:org.springframework.context.annotation.internalConfigurationAnnotationProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
// 查看当前类是否
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
**//** 调用getBean方法,该方法就是获取Bean如果获取不到就去创建一个bean,因此工厂后处理器的实例化和初始化都是要比普通Bean提前不少的。
//之后将创建好的工厂处理器放入集合中
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
//调用当前所有已经初始化的BeanPostProcessor,而ConfigurationClassPostProcessor也会在此调用,bean的扫描也会在此发生。
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
接下来我们就可以进入正题看看在这个后处理器的调用方法中是如何进行bean的扫描的:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
// 方法在进行了一些准备,主要是防止进行重复注册,上面的异常可以看到如何进行重复注册的话就会发生异常,这对我们来说无关紧要
processConfigBeanDefinitions(registry);
}
下面主要分析的就是postConfigBeanDefinitions这个方法,非常的长:
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
/**
挨个检查导入进来的配置类信息,这个配置可能就会是开始在程序入口传进来的配置类,比如在我们的示例程序中就是main
*/
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 这里会获取一个参数,这个参数是存放在beanDef的一个Map中的,凡是已经处理过的配置类都会Spring都会设置一个key为ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE,而value为lite或者full的属性值
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//*** 检查当前BeanDefiniton是否可以做为一配置类,这个方法在下面很多地方都有使用.(判断的依据下面单独展开)**
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;
}
// 根据order注解的值进行排序
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(
AnnotationConfigUtils.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);
// 正式成为Spring中的配置类
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 重要的一个解析方法
**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());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
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();
}
}
检查当前配置类是否有资格成为一个配置类
/**
* Check whether the given bean definition is a candidate for a configuration class
* (or a nested component class declared within a configuration/component class,
* to be auto-registered as well), and mark it accordingly.
* @param beanDef the bean definition to check
* @param metadataReaderFactory the current factory in use by the caller
* @return whether the candidate qualifies as (any kind of) configuration class
*/
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
// 下面的两个判断是用于区别BeanDef的类型,AnnotatedBeanDefinition和AbstractBeanDefinition 都是BeanDefinition的直接子接口是同级关系
// 在我们的示例中使用的是注解的方式进行配置,所以就会进入第一个判断.
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
// 在这个直接获取元数据,元数据中就是关于一个类的基础数据,包括它的类型,被什么注解标注了,还是有一些Spring默认给它填进去的一些属性
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
//
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = AnnotationMetadata.introspect(beanClass);
}
else {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " +
className, ex);
}
return false;
}
}
// 在我们获取到了当前类的元数据(metadata)之后,就会尝试通过元数据去看看当前类有没有被Configuration注解所修饰,并且会给配置类打个标识,这个标识就是刚刚说的:当前配置类是不是已经被处理过了
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
//如果被修饰了并且还在里面配置了proxyBeanMethods那么这个配置类标识的value就是full
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
//在为空的基础上可以在此判断他是不是另一种方式的配置类,也就是说如果没有被Configuratioin所修饰有没有被另外**四种注解**所修饰,这种情况下标识的value就是lite
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any. 这里就是这是一个order
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
上面所说的另外四种注解就是:
org.springframework.context.annotation.Importorg.springframework.stereotype.Componentorg.springframework.context.annotation.ImportResourceorg.springframework.context.annotation.ComponentScan
而Main类就是被Import和ComponentScan所修饰的.,因此它就会被认定作为一个配置类信息.而所有的配置类信息都会被收集到configCandidates这个集合中.在将所有的集合都收集完毕之后,接下来就要使用ConfigurationClassParser 将配置进行解析并且封装成为BeanDefinition.在解析时准备了两个集合:
- candidates: 候选的Bean这个集合中的BD将会被根据条件筛选一次
- alreadyParsed:筛选完毕的,这里BD基本上就会来接下来的流程中被实例化和初始化
解析配置类生成BeanDefinition
解析配置类的过程可能有点晕,因为它可能是一个循环调用的过程,可能跟着debug去走,过一会又回到了原来的方法…
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
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());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
--------------------------------------------------------------------------------------------------------------------------------------
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
//检查当前配置类是否应该跳过,跳过的标准就是是否被Conditoinal系列的注解所修饰,如果修饰了那就看一些是否满足满足了条件
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);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
**//*1 这里就核心的解析逻辑了**
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
*1 :对于sourceClass不为null的情况只有一种那就是解析的当前类存在父类,如果存在父类的话就会对父类进行解析
因为上面两个方法中都没有核心逻辑所以就给他放一起了。并且上面的方法在后续debug的过程中可能还会被看到.
/**
* Apply processing and build a complete {@link ConfigurationClass} by reading the
* annotations, members and methods from the source class. This method can be called
* multiple times as relevant sources are discovered.
* @param configClass the configuration class being build
* @param sourceClass a source class
* @return the superclass, or {@code null} if none found or previously processed
*/
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 是否被@Component修饰,并且会去处理当前Bean的内部类情况
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
// **memeberClass的意思就是内部类**
processMemberClasses(configClass, sourceClass, filter);
}
//是否被@PropertySource修饰
// PropertySource注解的作用就是读取指定目录下的properties文件
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或者ComponentScans修饰
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修饰,这个注解是用来导入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,也就是检查当前类的方法中有没有被@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;
}
所以在解析的过程中就被分为了一个个的流程,并且他们不是一个互斥的关系,如果你被@Configuration和@Import同时标注的话,它们两个注解都会参与解析,而不是解析了一个就返回了;下面就看看针对于不同的注解Spring是如何进行解析的,把代码块一个一个的提出来。
PropertySource注解解析:
// // 是否被@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");
}
}
在Spring中我们可以使用@Value(”${}”) 这种写法来获取.properties中的值并且注入到被修饰的属性中,但是首先我们应该引入这个属性文件,这个工作就可以由@PropertySource来完成:
@PropertySource("classpath:app.properties")
public class A{
}
所有的属性文件都会在这里进行解析并且加入到environment属性中,属性也是后续使用EnvironmentAware接口注入的属性:
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
// 获取解析路径,可能存在多个,在解析的时候使用,隔开
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
// 获取解析路径,一般情况下不会改变和location的值相等
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
// 通过resourceLoader将资源进行加载并且封装成Resource
Resource resource = this.resourceLoader.getResource(resolvedLocation);
// 在createPropertySource时,会去加载属性文件,并且将里面的内容封装成Map的形式:name--->小王,age---->18
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
}
}
--------------------------------------------------------
private void addPropertySource(PropertySource<?> propertySource) {
String name = propertySource.getName();
//注意这个propertySources 是从environment中取出的,因此它是环境变量的一个属性
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
// 存在覆盖的情况
if (this.propertySourceNames.contains(name)) {
// We've already added a version, we need to extend it
PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
if (existing instanceof CompositePropertySource) {
((CompositePropertySource) existing).addFirstPropertySource(newSource);
}
else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);
}
return;
}
}
if (this.propertySourceNames.isEmpty()) {
//将解析出来的属性配置添加进propertySources中的ResourcePropertySource中,至此就相当于添加进环境变量中,这样后续进行属性注入的时候就可以拿到属性值
propertySources.addLast(propertySource);
}
else {
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
propertySources.addBefore(firstProcessed, propertySource);
}
this.propertySourceNames.add(name);
}
ComponentScan的解析:
// 获取当前配置类的ComponentScans和ComponentScan的属性,并且封装为AnnotationAttributes,AnnotationAttributes中就是注解的属性单独提取了出来使用,使用起来就像是Map集合一样
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 =
**// 开始进行解析,如果被ComponentScan修饰的话他就是会指定一个扫描路径,就算没有指定Spring默认去给他指定一个**
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());
}
}
}
}
// **下面这个虽然看着一大串,实质上就是创建一个扫描器并且为该扫描器设置一些属性,这个属性值的设置就是根据ComponentScan中的属性进行配置的**
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
// 设置beanName的生成器
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
}
else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
// 设置一个scopeResolver
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
//设置一个资源路径通配符默认
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
//包含策略
for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {
List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,
this.resourceLoader, this.registry);
for (TypeFilter typeFilter : typeFilters) {
scanner.addIncludeFilter(typeFilter);
}
}
//排除策略
for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {
List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,
this.resourceLoader, this.registry);
for (TypeFilter typeFilter : typeFilters) {
scanner.addExcludeFilter(typeFilter);
}
}
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
// 基础扫描路径
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
//如果没有显示的去配置扫描路径的话,就会拿当前配置类的包路径作为扫描路径
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
// 开始根据设置的路径进行扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
}
doScan():
具体扫描分为了两大块:一个是根据路径寻找候选的BeanDefinition,第二块就是根据scanner的配置对BeanDefinition进行一次操作
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 查询候选的BeanDefinition,里面会有一个中间方法,用来做一个判断,我们直接进入最关键的执行:scanCandidateComponents
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 对候选的BeanDefiniton进行解析,也就是为它设置一些属性,比如scope、benaName、lazyInit,检查是否被Primary、DependsOn、Role注解所修饰
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 对候选Bean进行检查,检查候选Bean是否已经存在于BeanFactory中,如果已经存在就不会做任何操作,如果不存在就将其注册到BeanFactory中
if (checkCandidate(beanName, candidate)) {
// 封装一下
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册到Bean工厂也就是beanDefinitionMap中,这样本次的扫描就结束了。
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
----------------------------------------------------------------scanCandidateComponents--------------------------------------------------------------------------------------------------
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 在这里会组装一个扫描路径,最重要的自然是我们指定basePackages,之后还有两个属性
// 一个是前缀classpath*: 表示将会对当前项目以及引用jar包中路径进行扫描
// 另一个就是**/*.class:将会扫描该目录下的所有子目录
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// 通过一个资源解析器,将路径下的所有class文件解析出来,这里使用的资源解析器是PathMatchingResourcePatternResolver,也就是可以根据通配符进行路径的扫描。
// 资源解析器就是Spring为了统一从不同的地方获取资源的一个抽象,这个获取资源的地方可能来自磁盘、网络等等地方,而Spring会将获取到的资源统一解析为Resource对象
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
try {
//将resource继续封装成MetadataReader,在Spring有很多MetadataXXXX,他们的目的都是大同小异,都是将传进去的参数进行扁平化,将参数中的属性或直接赋值或将其进行加工或里面预先定义了Spring的默认属性,归根结底都是为了符合后续的使用,
// 并且也是方便后续的使用。
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 这个判断方法就是将excludeFilter和includeFilter应用到候选resource上,来判断当前resource是否满足规则
if (isCandidateComponent(metadataReader)) {
//封装为sbd,各种各样的BeanDefinition中的一种
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
//对sbd进行判断,比如他是不是抽象的,是不是一个接口
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
// 将sbd添加到集合中
candidates.add(sbd);
}
**.........删除一些debug和异常**
return candidates;
}
------------------------------------------------------------------------checkCandidate------------------------------------------------------------------------
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
//检查当前Bean工厂中是否时候已经存在这个Bean
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
// 如果相同的BeanName已经存在于Bean工厂中,就会尝试进行兼容
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null) {
existingDef = originatingDef;
}
// 判断是否可以兼容,一共有三种条件,满足一个即可,newBd就是newBeanDefinition
//1. newBd是否不是从扫描(通过配置ComponentScan来扫描获取的Bean封装的BeanDefinition的类型就是ScannedGenericBeanDefinition)中获取的而是从其他方式获取
//2. 他们的class是否是一样的
//3. 他们本身就是一样的只不过被扫描到了两次
if (isCompatible(beanDefinition, existingDef)) {
return false;
}
// 如果无法被兼容就会抛出异常
throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}
在CompoentScan第一次解析完毕之后,他就会返回本次注册到BeanFactory中的BeanDefinitionHolder,也可以近似的看作返回的就是BeanDefiniton,而且这BeanDefiniton中可能还有被ComponentScan、Configuration注解修饰的类,因此在完成第一次的扫描注册之后,紧接着就开始对这些已注册的Bd进行检查,我们把最上面的代码拿过来
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) {
// 解析完成之后就会返回一个BDH(BeanDefinitionHolder)的集合,就是已经注册到BeanFactory的BeanDefinitoin
Set<BeanDefinitionHolder> scannedBeanDefinitions =
**// 开始进行解析,如果被ComponentScan修饰的话他就是会指定一个扫描路径,就算没有指定Spring默认去给他指定一个**
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 然后开始对BDH进行逐个检查
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
//将BD取出来
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
//这里的这个方法是不是就很熟悉,就是在开始的检查配置的那个方法。
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
}
}
}
}
由此一来在调用ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory) 后发现当前BeanDefinition符合作为一个配置类的条件,就会针对这个类在此进行解析,这样一来就会开启一个递归调用,上面所讲述的步骤则会再此发生。
但是这个递归只是针对@ComponentScan,在子类依旧存在ComponentScan的情况下会一直递归查找路径下可能存在的Bean。
注意:Component'Scan的扫描逻辑只能针对Component系列注解和Configuration,换句话说扫描路径下类直接标注@Import、@ComponentScan、@ImportResource是没有用的!
@Import注解的处理
被Import修饰的类会首先去检查是否为ImportSelector或者ImportBeanDefinitionRegistrar这个似乎就和springboot的自动导入有关系了。但是被Import进来的类是没有beanName的并且也没有真正的注册到BeanFactory中,这个BeanName会在后续进行生成。被Import导入的类会有三种情况:
- ImportSelector这种方式可以通过返回一个String数组,数组中的值就是要导入Bean对象的全限定名称。
- ImportBeanDefinitionRegistrar接口可以拿到
BeanDefinitionRegistry,可以更加直观的注册Bean对象,但是这种方式的注入需要对Spring中BeanDefiniton的有一定的了解。 - 就是普通Bean的注入
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 当前类是否实现了ImportSelector
if (candidate.isAssignable(ImportSelector.class)) {
// 如果实现了ImportSelector的化就去获取该类的class对并且进行实例化
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
// 下面分为两种情况,一种是DeferredImportSelector是ImportSelector的子类
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
// 另一种就是它本身
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// ***1** 这里进行了递归调用
**processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);**
}
}
// 当前类是否实现了ImportBeanDefinitionRegistrar
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 在处理实现的时候,它并没有进行接口方法的调用而是将他加入到configurationClass中的importBeanDefinitionRegistrars的Map集合中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// ***2 并且在这里进行递归调用**
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
*1:处方法进行了递归调用的原因是防止通过Import导入的类中还存在被@Import修饰的类,通过这种深度优先搜索的方式来检查所有导入的类。
*2 :在导入的类没有特殊条件的时候,就将该类看作一个配置Bean进行处理,但是并不会想ComponentScan那样直接注入进行BeanFactory,而是设置当前ConfiguratioinClass的ImportBy属性标识当前类是被Import导入进来的,回头在
this.reader.loadBeanDefinitions(configClasses)中进行处理。还有一点就是通过ImportSelector接口导入的Bean,最后其实是在这个代码块中被处理。
对于ImportBeanDefinitionRegistrar处理,仅仅是进行了类的实例化并且加入到@Import所修饰的类的ConfigurationClass的属性中。
比如下面这个例子,因为B导入了A,A实现了ImportBeanDefinitionRegistrar,所以会将A进行实例化并且添加到B的ConfigurationClass的importBeanDefinitionRegistrars的Map属性中。同时也没有进行方法的回调,这就说明如果在A上面继续标注Import注解话是不能够生效的。
class A implement ImportBeanDefinitionRegistrar{
.... 省略需要实现的方法
}
@Import(A.class)
class B{
}
@Bean注解的处理
通过以上@Bean和@Import方式注入的Bean此时并没有BeanName,并且也没有被注册到beanDefinitionMap中,此时它们现在都被放入一个在解析完毕后会返回一个configClass的集合中,在后续的ConfigurationClassPostProcessor#processConfigBeanDefinitions 中继续这两种类型的Bean进行处理并且注册:
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
在*1处会解析当前配置类中所有被@Bean所修饰的方法,并且将该这些方法的元信息(MethodMetadata)加入到beanMethods属性中
最后我们把最上层的方法的解析片段拿过来,也就是
// 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 {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates);
parser.validate();
// 获取本次解析完成的配置Bean
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 移除上一轮解析到的配置Bean
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());
}
// 加载@Bean,@Import形式的Bean
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
candidates.clear();
------------------------------------------------***1-**-----------------------------------
if (registry.getBeanDefinitionCount() > candidateNames.length) {
// 取出当前的工厂中的BeanDefinitonNames
String[] newCandidateNames = registry.getBeanDefinitionNames();
// 封装为老的BDNames
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) {
// 如果是新加入的BeanDefiniton就为true
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());
我们看到在解析完成之后就会调用上面所说的this.reader.loadBeanDefinitions(configClasses) ,configClasses本就是ConfigurationClassParser 中的成员变量configurationClasses ,也就是本次解析了多少配置Bean。
着重解释一下*1那块代码的含义,也是是否开启递归调用的重要条件:
首先我们要先关注一个变量candidateNames 这个变量是在刚进入方法的时候提前取出来的,在经历了下面的解析之后BeanFactory中难免会有新的BD注册到工厂中,因此我们可以将这个candidateNames 属性看为老的BDNames。
之后获取了当前BeanFactory中注册了多少BD,并且和老的BD数量比较,如果大于的话就说明本次循环中产生了新的BD进入了工厂中,因此我们就需要将新的BD提取出来进行检查,检查的标准有两点:
- 当前BD满足作为配置Bean的条件:也就是被Configuration、Component、Service、Repository、Controller注解修饰了。
- 当前BD没有被解析过了
最后将本轮满足成为配置类条件的BD收集到一个candidates 中,这样在while(!candidates.isEmpty()) 判断如果集合是非空的那么就针对当前的配置类进行在此进行一轮操作。
一般情况下:如果会进入下一轮的话就是在
AnnotationConfigApplicationContext中指定了多个配置类。否则的话一般不会进入第二次循环。