本文目录
-
- 前言
- SpringBoot启动的时候都做了一些什么事情?
- @SpringBootApplication注解结构
- @EnableAutoConfiguration、开启自动装配
- @AutoConfigurationPackage(确定扫描组件的范围)
- AutoConfigurationImportSelector <==> DeferredImportSelector
- DeferredImportSelector 源码开始解读
- 研究deferredImport、import源码入口
- parse方法是如何解析 BeanDefinition的?(processConfigurationClass方法)
- 真正处理解析configClass的地方(doProcessConfigurationClass)
- 解析启动类上的@ComponentScan注解源码探究
- 解析启动类上的@Import注解源码探究(processImports)
- 小结processImports()方法
- DeferredImportSelector源码解析
- DeferredImportSelector 怎么知道要装配哪些类的BeanDefinition(答)
- DeferredImportSelector是如何注入这些类的BeanDefinition?
- BeanDefinitionMap中的数据从哪来的?(loadBeanDefinitions)
- 个人阅读spring源码后的感受
- 附页之importSelector与DeferredImportSelector 的差别?
- 附页之Bean实例化所需的BeanDefinition从何而来?
前言
说到SpringBoot我们肯定不陌生,自动化的配置让我们在开发的过程中使用的十分爽,例如传统的mvc项目开启aop需要在xml文件中写上一大坨,而在SpringBoot项目中只需加上@EnableAspectJAutoProxy注解就完事了,还是那句话我们不应该只局限于会用,更应该懂原理,这样日后涉及到高阶的知识、或者我们需要横向扩展的时候将会顺畅很多。
SpringBoot启动的时候都做了一些什么事情?
曾几何时不知读者有没有思考过,为什么我们只要书写如下的一个SpringBoot启动类,项目中所有被@Component、@Configuration…注解的类就会被当做Bean来解析,注入到 IOC容器中去了,why?下图是一个很简单SpringBoot项目的启动类。简简单单一个@SpringBootApplication 注解帮我们搞定了所有的事情
@SpringBootApplication注解结构
@EnableAutoConfiguration:开启自动装配(不算怎么算本文的核心内容)
@ComponentScan:解析被@Component注解的标注的类,当做一个Bean注入到IOC容器中 (里面配置了一些过滤规则,不在本文讨论范围)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@EnableAutoConfiguration、开启自动装配
@AutoConfigurationPackage:自动装配作用于哪些包(具体的这个自动装配的范围是多大,下文分析)
@AutoConfigurationImportSelector.class:导入 AutoConfigurationImportSelector 组件(具体这个组件在哪里解析的,下文分析!!! 如果读者着急可以直接跳到本文目录:解析启动类上的@Import注解源码探究)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage(确定扫描组件的范围)
导入 Registrar的这么一个组件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
Registrar作用:给容器中注入了一个BeanDefinition对象(封装了描述bean的各种信息的一个对象),debug进去看一下
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
可以看到我们的BeanDefinitionRegistry(BeanDefinition注册表)中会将已经是bean形态的BeanDefinition进行一个记录,注册到BeanDefinitionRegistry中。从图中我们可以看到我们com.zzh.payment 以及下面的子包符合条件的类都被当做 bean 注入到IOC容器中。
AutoConfigurationImportSelector <==> DeferredImportSelector
可以看到 AutoConfigurationImportSelector 本质为一个 DeferredImportSelector, DeferredImportSelector实现了ImportSelector 接口,效果和 ImportSelector 差不多都是给 IOC 容器中注入一些 BeanDefinition(注意只是注入BeanDefinition,并没有进行实例化Bean),至于差别下文具体分析。下面来思考俩个核心问题
- DeferredImportSelector 怎么知道要注入哪些 类 的BeanDefinition?
- DeferredImportSelector 是如何注入这些类 的 BeanDefinition?
DeferredImportSelector 源码开始解读
emmm想了一会如何接着写下文,如果不先搞懂@import的源码着实无法将DeferredImportSelector的源码给说明白,所以下文的源码部分有点长,并且这部分会牵涉到spring的源码部分。所以想要了解 @import、 DeferredImportSelector、ImportSelector、ImportBeanDefinitionRegistrar、@ComponentScan 的源码,debug进入下图标黄的地方就好了,spring在实例化Bean之前会做一些准备工作,调用BeanFactory的后置处理器(这些后置处理器的作用就是,通过解析@Configuration、@Component.、@Bean、@Import…这些注解以及DeferredImport、ImportSelector …这些接口,获取对应的BeanDefinition信息,然后将所有的BeanDefinition放入一个叫做beanDefinitionMap的容器中,最后在this.finishBeanFactoryInitialization() 中取出beanDefinitionMap中所有的BeanDefinition进行实例化Bean)
debug来到 invokeBeanFactoryPostProcessors()方法中,调用各种后置处理器,如果beanFactory是BeanDefinitionRegistry类型,从beanFactory中拿出ConfigurationClassBeanPostProcessor 的一个bean,然后紧接着去调用它(这个后置处理器的意思,顾名思义就是初始化处理和配置有关的类),注意里面这段代码 beanFactory.getBean(ppName,BeanDefinitionRegistryPostProcessor.class)涉及到Spring Ioc的源码,没读过Ioc源码的读者,可以这么简单理解:如果没有Bean就创建一个Bean然后返回,如果不存在Bean那么会先创建一个Bean,然后返回。总之一定会返回对应名字的一个Bean出来
值得一提的是,调用后置处理器的顺序是,先是处理实现了 PriorityOrdered 接口的、其次是处理实现了 Ordered接口的、最后处理什么接口都没有实现的后置处理器。如果beanFactory是ConfigurableListableBeanFactory类型,那么后置处理器执行依然是顺序PriorityOrdered 》Ordered 》other
接着debug进入invokeBeanDefinitionRegistryPostProcessors()来到 processConfigBeanDefinitions(),看看spring是如何解析我们的配置类的,利用我们的解析器 ConfigurationClassParser 去解析图中黄色对应类(SpringBoot项目的启动类 PaymentApplication )的 BeanDefinition 信息,具体的解析过程debug进入 parse()方法
研究deferredImport、import源码入口
大体可以分为2种情况来解析 BeanDefinitionHolder:
- 解析由注解方式注入的类(被@import、@Bean、@Configtion、@RestController…标注的类 )
- 解析由自动装配注入进来的类(META-INF目录下spring.factories文件中的类)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
//获取配置类
Iterator var2 = configCandidates.iterator();
while(var2.hasNext()) {
//获取配置类Holder
BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();
//通过Holder来获取配置类的BeanDefinition
BeanDefinition bd = holder.getBeanDefinition();
try {
//解析注解方式注入的类
if (bd instanceof AnnotatedBeanDefinition) {
//解析配置类、import、compentscan等源码在里面有体现
this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {
this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());
} else {
this.parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException var6) {
throw var6;
} catch (Throwable var7) {
throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7);
}
}
//解析自动装配注入进来的类
this.deferredImportSelectorHandler.process();
}
注释写的很详细了,可以看到SpringBoot的启动类PaymentApplication的BeanDefinition是 AnnotatedBeanDefinition类型的,接着debug,进入 this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName()) ,开始分析@Import的源码。
parse方法是如何解析 BeanDefinition的?(processConfigurationClass方法)
parse()方法本质为 processConfigurationClass()方法,processConfigurationClass()方法将我们的 BeanDefinition包装成了一个ConfigurationClass 对象,然后来到 processConfigurationClass 方法中,不着急debug,先来分析一下源码的大体结构,可以先看一下注释
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
//判断configClass是否需要跳过解析
if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);
//如果configurationClasses中已经存在configClass
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
//configClass、existingClass都没有被import的情况下,将已有的configClass,与configurationClasses中的configClass进行合并
existingClass.mergeImportedBy(configClass);
}
return;
}
//configClass不是被import导入,但是已经存在于configurationClasses,移除当前configClass
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter);
do {
//真正处理我们配置类的地方、核心
sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);
} while(sourceClass != null);
//将configClass放入 configurationClasses、圈重点
this.configurationClasses.put(configClass, configClass);
}
}
顺着主脉络走,发现spring会将我们的主启动类(PaymentApplication)榨干,只要 sourceClass != null,那么将会一直循环执行this.doProcessConfigurationClass(configClass, sourceClass, filter),最终将我们的configClass(PaymentApplication)存入configurationClasses这个Map集合中,表示你已经被我解析过了
真正处理解析configClass的地方(doProcessConfigurationClass)
doProcessConfigurationClass方法源码
@Nullable
protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
this.processMemberClasses(configClass, sourceClass, filter);
}
Iterator var4 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();
AnnotationAttributes importResource;
while(var4.hasNext()) {
importResource = (AnnotationAttributes)var4.next();
if (this.environment instanceof ConfigurableEnvironment) {
this.processPropertySource(importResource);
} else {
this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
}
}
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
Iterator var14 = componentScans.iterator();
while(var14.hasNext()) {
AnnotationAttributes componentScan = (AnnotationAttributes)var14.next();
Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
Iterator var8 = scannedBeanDefinitions.iterator();
while(var8.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var8.next();
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
this.parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
String[] var20 = resources;
int var22 = resources.length;
for(int var23 = 0; var23 < var22; ++var23) {
String resource = var20[var23];
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass);
Iterator var18 = beanMethods.iterator();
while(var18.hasNext()) {
MethodMetadata methodMetadata = (MethodMetadata)var18.next();
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
this.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);
return sourceClass.getSuperClass();
}
}
return null;
}
解析启动类上的@ComponentScan注解源码探究
在解析我们的主启动类之前,会先对SpringBoot项目启动类上的@ComponentScan注解进行解析,根据解析到被@Component标注的类的的BeanDefinition,重复执行上文分析过的Parse方法。
可以看到我项目中所有被@Component、@Configuration 、@RestController、@Import…这些注解修饰的类,都被@ComponentScan注解给扫描进来了,然后逐个解析。
解析启动类上的@Import注解源码探究(processImports)
解析完了@ComponentScan注解,然后来解析我们的Import注解了,debug进入this.processImports()。其实无论是解析什么注解,最终都会执行同一个方法,这里先卖个关子,先不说
先不着急分析this.processImports()的源码,我们先来关注一下,它里面的入参是些什么东西,what fark?this.getImports(sourceClass)?????
其实 this.getImports(sourceClass),就是获取主启动类上面所导入的Import组件,其中不乏开篇(@EnableAutoConfiguration、开启自动装配)说过的 AutoConfigurationImportSelector 类,此类即为自动装配的核心,原来你是在这里进行解析的啊!!!!!!!o my gad
对照我们的启动类来看,是不是瞬间清楚了很多呢
现在开始分析正文,来到 processImports()方法咯,这里的代码和@Import、ImportSelector 、ImportBeanDefinitionRegistrar 、DeferredImportSelector的源码有关系,不着急分析源码,先来大致过一遍源码的大体脉络
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
if (!importCandidates.isEmpty()) {
if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
} else {
this.importStack.push(configClass);
try {
Iterator var6 = importCandidates.iterator();
while(var6.hasNext()) {
ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next();
Class candidateClass;
if (candidate.isAssignable(ImportSelector.class)) {
candidateClass = candidate.loadClass();
ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
} else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
} else {
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
} catch (BeanDefinitionStoreException var17) {
throw var17;
} catch (Throwable var18) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18);
} finally {
this.importStack.pop();
}
}
}
}
观察源码我们可以发现源码的设计思路为:挨个遍历由主启动类上注解注册的组件,如果组件类型为 ImportSelector,获取 selectImports方法中的导入类名,然后将其封装成一个SourceClass对象,接着对SourceClass 进行解析、也就是执行processImports(SourceClass)方法,之后就是对 SourceClass进行下图中的判断了。
来到ImportSelector分支后,还会继续判断组件类型如果是 DeferredImportSelector 类型的,进行对应的操作。
接着我们的debug进入 this.deferredImportSelectorHandler.handle()方法,可以看到我们的 AutoConfigurationImportSelector 此时已经存进 deferredImportSelectors中了,记在小本本上面
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
ConfigurationClassParser.DeferredImportSelectorHolder holder = new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
} else {
//将配置类存入 deferredImportSelectors 中
this.deferredImportSelectors.add(holder);
}
}
如果是 ImportSelector 类型的组件进行如下操作
如果是 ImportBeanDefinitionRegistrar 类型的进行如下操作,将组件的Class存放到 importBeanDefinitionRegistrars 的一个map中
如果是 普通类型 进行如下操作,再次回到了 processConfigurationClass(),会再次经历对该类进行@Component、@Import、DeferredImportSelector、ImportSelector、ImportBeanDefinitionRegistrar相关的解析操作,那问题来了。什么时候终止这个循环呢?
其实对该类进行@Component、@Import、DeferredImportSelector、ImportSelector、ImportBeanDefinitionRegistrar的解析是有一个前提条件的,那就是 sourceClass!=null,但是如果是普通类那么 sourceClass=null,也就终止循环了,直接将这个普通类型的configClass存入configurationClasses中
小结processImports()方法
processImports方法会对我们的类进行一系列抽丝剥茧的递归解析操作,如果发现你是ImportSelector类型的类,那么会接着解析你对应selectImport()方法中的类,如果selectImport()方法中还存在ImportSelector类型的类,接着递归解析,直到selectImport()方法返回的全是一些普通的类,最后将这些普通类的BeanDefinition,包装成一个configClass对象存入configurationClasses这个map中去。但是此时自动装配的类的BeanDefinition并未存入configurationClasses中,而是在this.deferredImportSelectorHandler.process()中,才将自动装配的类的BeanDefinition存入configurationClasses,具体存放过程下文有解析。
等我们解析完启动类注册进来的所有组件之后,最终会return null哦!!!!!return null也就意味着当前的configClass已经解析完毕,可以存入configurationClasses了。(标志着主启动类以及主启动类身上的所有组件已经解析完毕,并且对应的configClass分类存进了不同的Map中)
- @import、importSelector、普通类、类型的 BeanDefinition最终经过包装,存入名字为configurationClasses 的map中
- DeferredImportSelector类型的 BeanDefinition 最终经过包装,会存入 名字为 deferredImportSelectors 的map中
- ImportBeanDefinitionRegistrar 类型的 BeanDefinition最终经过包装,会存入为一个名字为 importBeanDefinitionRegistrars 的Map中
DeferredImportSelector源码解析
言归正传,回到我们的parse方法中,终于debug到了这里,此时由注解方式注入的类的BeanDefinition已经解析完成了,(开始解析自动装配进来的类)进去看看咯,源码贴图如下在此
图中蓝色箭头还是跳过。。。。。。。本人画错了图,懒不想改了。。。。。在解析注解注入的类的BeanDefinition时,已经将DeferredImportSelector类型的类的存入到deferredImportSelectors中了。所以debug会来到handler.processGroupImports() 中。
其实processGroupImports中做的事情很简单,根据对应的gruop解析group内含的DeferredImportSelector组件,调用对应的getImports()方法(获取要自动装配类的一些定义信息,封装在entry对象里面),然后对每个entry对象再次进行processImport()方法的调用,最终自动装配的类的BeanDefinition也会存入configurationClasses中。至于processImport()方法上文已经分析过了。现在重点来分析DeferredImportSelector中的getImports()方法。
DeferredImportSelector 怎么知道要装配哪些类的BeanDefinition(答)
还记得文章开头我提出的俩个问题吗,DeferredImportSelector 怎么知道要装配哪些类的BeanDefinition?答案就在下图中的代码里面,别着急一步步开始分析。
debug进入this.group.process()方法中,方法很简单将需要自动装配的类,封装成AutoConfigurationEntry对象存入autoConfigurationEntries中,那问题来了,这个autoConfigurationEntries在哪里使用呢?getAutoConfigurationEntry()?从哪里获取要自动装配的类?
getAutoConfigurationEntry方法没啥好看的,看下面的图片就好了
获取候选的要自动装配进来的类,点进去SpringFactoriesLoader.loadFactoryNames()瞧瞧吧,但是在此注意一下入参吧,入参如下是不是觉着有点熟悉呢?不就是EnableAutoConfiguration 吗,仔细瞧瞧下面几张图相信读者定会豁然开朗
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
return configurations;
}
接下来就是开始从spring.factories文件读取EnableAutoConfiguration节点的valus了,这些类会在之后的IOC容器初始化的时候,对其进行实例化,注入到IOC容器中
可以看到我们的b类已经被扫描进来了,最终 “com.zzh.payment.testBean.b”这个字符串会被封装成一个AutoConfigurationEntry对象存入autoConfigurationEntries这个map中,到此回到本小节的这个问题就已经解答了哦。
DeferredImportSelector是如何注入这些类的BeanDefinition?
言归正传,我们的第一步已经完成了,开始我们的第二步了。
源码如下selectImports。也是在AutoConfigurationImportSelector类中,将autoConfigurationEntries中的对象挨个遍历,再次封装成一个entry对象的迭代器返回
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
} else {
Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
return new Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
}).collect(Collectors.toList());
}
}
最终对每个enrty对象挨个processImport(),在调用完最终b这个类会被放入configurationClasses中
我这里用了一个条件断点,条件为((ClassPathResource)configClass.getResource()).getPath().equals(“com/zzh/payment/testBean/b.class”)
等到所有需要自动装配的类都解析完毕后,来到 this.reader.loadBeanDefinitions(configClasses) 方法,开始将所有需要注入到IOC容器的BeanDefinition,放入BeanDefinitionMap中
BeanDefinitionMap中的数据从哪来的?(loadBeanDefinitions)
挨个遍历configurationClasses中的configClass,然后进行加载
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator trackedConditionEvaluator = new ConfigurationClassBeanDefinitionReader.TrackedConditionEvaluator();
Iterator var3 = configurationModel.iterator();
while(var3.hasNext()) {
ConfigurationClass configClass = (ConfigurationClass)var3.next();
this.loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
打断点进入loadBeanDefinitions()方法后,中间不是很重要的过程就先省略了,最终会来到下图这,当然我这里使用了一个条件断点 ((ClassPathResource) configClass.getResource()).getPath().equals(“com/zzh/payment/testBean/b.class”),才定位到的
最终将类的各种信息都进行整理一下,设置bean的作用域、bean的名字啊等等、最终进行注册BeanDefinition
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
configBeanDef.setScope(scopeMetadata.getScopeName());
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//进行注册BeanDefinition
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
configClass.setBeanName(configBeanName);
if (logger.isTraceEnabled()) {
logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
}
}
然后我们的b类就被注册成功了。到此,自动装配的整套流程就已经完成了
个人阅读spring源码后的感受
- 自动装配对标springboot项目与xml的,其实更像是扮演xml的角色存在,因为它不是基于注解扫描加载的,他需要依赖文件。
- 开启自动装配从严格意义上来说,并不是开始IOC对bean的注入,开启自动装配仅仅是将所需注入IOC容器的类的BeanDefinition注入到BeanDefinitionMap中,真正的从类到bean的过程,还需要走完Bean生命周期中的实例化那一环
- 自动装配个人感觉有点像懒加载,其实META-INF目录下的spring.factories文件,在项目启动的时候就已经经过扫描解析,并存入本地缓存中去了(一个cache的map),当我们开启自动装配的时候,有优先从缓存中获取对应的信息
- 要想真正读懂源码不亲自debug一遍,真的是一知半解,本文也只是粗略的讲述了一下springBoot关于注入方面注解的大体解析过程。
- 了解自动装配的原理后,其实我们也就知道,我们引入的maven依赖的jar包的工作原理了,因为在springBoot项目启动的时候,会自动扫描项目,以及jar包中的所有bean,然后注入到IOC容器中,我们在开发的时候,即使你没有配置过相关的bean,但是也能有默认得bean可以使用,原因就是jar包中配置的bean被自动装配到我们的IOC容器中来了哦,其实这也就是starter启动器的核心之处了
附页之importSelector与DeferredImportSelector 的差别?
- 执行时机的不同:importSelector类型的类的BeanDefinition会先注册到BeanDefinitionMap中
- DeferredImportSelector 在进行注册BeanDefinition之前会对这些类的BeanDefinition进行一个排序
附页之Bean实例化所需的BeanDefinition从何而来?
结合之前我阅读spring IOC的源码经验,贴几张图供大家参考。发现原来@Import注解也不过是为bean实例化做准备嘛
分析源码不易,点个便是给予我最大的动力
撒花---------------------