承接上文 : ConfigurationParser#parse(Set<BeanDefinitionHolder> configCandidates)
进入到之后 看到会有几个选择 无非是通过@Configuration 还是通过xml方式什么定义的Configuration 由于现在绝大部分都是以注解形式 我直接就直接进入注解 其他的都是大差不差 基本上都差不太多 区别在于拿属性的时候方式不一致
进入到 ConfigurationParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 判断是否跳过 这个是跟@Conditional接口相关
//@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
//@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)
//@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
//@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
//@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
//@ConditionalOnNotWebApplication(不是web应用)
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// ... 省略掉不重要代码
do {
//此方法很重要
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// ...
// 如果配置类 被@PropertySource修饰 则将配置的文件导入进environment
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");
}
}
// 处理扫描 会根据 你再@CompScan里面配置的路径拿到所有class文件
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
// 扫描类路径下 class文件 封装成包装类BeanDefinitionHolder
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();
}
// 判断当前类是否是@Configuration 如果是的话 则当成@Configuration递归处理
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 此方法很重要单独讲解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 支持配置文件 新增beanDefination
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
// ...
// 处理@Configuration的@Bean注解 生成BeanDefination
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
//处理接口中所有添加@Bean注解的方法,内部通过遍历所有接口,解析得到@Bean注解方法,并添加到configClass配置类信息中
processInterfaces(configClass, sourceClass);
// 处理该类的超类
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
我把重要方法过程简单标注一下 : 首先 针对扫描 我们需要先了解一下 注解的别名机制 : 例如 @SpringBootApplication里面有String[] scanBasePackages() default {}属性;
同时该属性上有@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")这个一段代码 意思是 将SpringBootApplication的scanBasePackages映射到ComponentScan的basePackages属性上 (!!!!!!!!!! 前提是SpringBootApplication得被ComponentScan修饰)
如果扫描代码一直跟进去 挥发现进入到ClassPathScanningCandidateComponentProvider#scanCandidateComponents 我就不贴代码了 直接给出结论 这个方法会拼接一个classpath下的满足条件(basePackages路径)的 .class文件地址(编译时期生成的) 会拿到这个文件此时是二进制 我们需要ClassReader类来读取二进制流 生成beanDefination并生成包装类
第二个重点 !!!!! 此方法专门处理@Import注解 processImports(configClass, sourceClass, getImports(sourceClass), true); 就是此方法存在 实现了我们很多Spring相关匪夷所思的事情 (有感而发 : 其实客观讲 时至今日我看Spring代码好久了 给我最大感受 不是设计模式 或者调用一些api等等很厉害(随着实践这些都是真的不是很难) (当然了我并没有说在这些方面Spring做的不好) 最让我感慨感觉到厉害的是 Spring作者在抽象事物上的能力 抽象事物 大家肯定的都说 但是这是一个看起来很简单 但你真正接触起来发现 这个要做的好 做的张弛有度 真的很难很难很难)
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// ...
// 增加缓存
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 判断接口 是否是ImportSelector子类
if (candidate.isAssignable(ImportSelector.class)) {
// ...
//将各种aware接口赋值 比如实现了BeanFactoryAware 会自动将当前容器注入进去
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
//如果这个实现类也实现了DeferredImportSelector接口
//,就被加入到集合deferredImportSelectors中 !!!!!! 此事没有没有调用selectImports
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(
configClass, (DeferredImportSelector) selector);
}
else {
// 调用不是DeferredImportSelector的ImportSelector接口
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 如果导入的类是ImportBeanDefinitionRegistrar 把它加进全局唯一的ConfigurationClass里面
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 =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
//否则当成@Configuration类处理 递归操作
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
}
}
首先针对几个接口 进行讲解
1 . @ImportSelector : 入参为注解元数据 会根据@Configuration类上的所有注解传入进行生成我们想要导入的包路径类名 !!! 注意 此时是没完全处理完@Configuration
2 . @DeferredImportSelector : 跟@ImportSelector相比 他是在处理完所有@Configuration内的方法 包括@ImportResource 以及@Bean等 同时支持分组 , 组内排序执行顺序
3 . @ImportBeanDefinitionRegistrar : 允许往BeanDefinationRegistry里面注册BeanDefination (!!!!!!!!! 比如apollo就注入了很多处理程序 等等 很重要的接口)
讲到这基本上parse方法就ok了 没什么 注册BeanDefination到这基本上就差不多了
下一节 SpringBoot源码解析系列(4) SpringBoot的自动扫描功能: https://blog.csdn.net/weixin_44669461/article/details/116135443?spm=1001.2014.3001.5501