共计 17195 个字符,预计需要花费 43 分钟才能阅读完成。
在 Spring 利用上下文筹备阶段 prepareContext()办法将利用的启动类加到 Context 中。
在 Spring 利用上下文启动阶段,会进入到 refreshContext()办法,具体代码执行流程如下:
看 ServletWebServerApplicationContext 的类图:
ServletWebServerApplicationContext 间接继承自 AbstractApplicationContext,所以最终会进入到 AbstractApplicationContext#refresh()办法。
走到 AbstractApplicationContext#refresh()办法便意味着 Spring 利用上下文进入 Spring 生命周期,Spring Boot 外围个性随之启动,比方:主动拆卸。
三、解决主动拆卸
1、解决主动拆卸的入口
最终进入到 PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory,List<BeanFactoryPostProcessor>)办法中,主动拆卸在其中实现;
invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory,List<BeanFactoryPostProcessor>)办法中,传入接管到的入参 beanFactory 类型为 DefaultListableBeanFactory:
再看 DefaultListableBeanFactory 的类图:
其实现了 BeanDefinitionRegistry 接口,所以会进入到 if 代码块:
在进入 if 代码块之后,会做两个操作:
1> 首先,遍历传入的三个 BeanFactoryPostProcessor 对其做分类;
分为惯例后置处理器汇合 regularPostProcessors 和 注册处理器汇合 registryProcessors;
分类之后,regularPostProcessors 有一个成员,registryProcessors 中有两个成员。
final class PostProcessorRegistrationDelegate {
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
....
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 这里只有一个值:org.springframework.context.annotation.internalConfigurationAnnotationProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
// internalConfigurationAnnotationProcessor 实现了 PriorityOrdered 接口
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 将 ConfigurationClassPostProcessor 增加到 currentRegistryProcessors 中
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 对 currentRegistryProcessors 做一个排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 走到这里 registryProcessors 中有三个对象了
// todo 外围所在
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
....
}
}
接着从 BeanFactory 中获取到 BeanDefinitionRegistryPostProcessor 的实现类 ConfigurationClassPostProcessor,并将其增加到 registryProcessors 中;此时 registryProcessors 中有三个成员:
SharedMetadataReaderFactoryContextInitializer 的动态外部类 CachingMetadataReaderFactoryPostProcessor;
ConfigurationWarningsApplicationContextInitializer 的动态外部类 ConfigurationWarningsPostProcessor;
ConfigurationClassPostProcessor;
2> 其次,执行以后注册处理器 ConfigurationClassPostProcessor;
代码执行流程如下:
因为 postProcessors 中只有一个成员 ConfigurationClassPostProcessor,进入到 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)办法。
从 ConfigurationClassPostProcessor#processConfigBeanDefinitions(BeanDefinitionRegistry)办法开始真正进入到解决主动拆卸的外围逻辑。
2、解决主动拆卸内容
1)找启动类,构建 ConfigurationClassParser 解析器筹备解析启动类
首先从 DefaultLisableBeanFactory 中获取所有曾经注册的 BeanDefinition 名称;
candidateNames 中蕴含了咱们的启动类,此外还有 6 个 internalXxx 类;而后遍历找到启动类,将其加到 configCandidates 汇合中。
找到启动类(saintSpringBootApplicatioin)之后,构建一个配置类解析器 ConfigurationClassParser,其中包含 ComponentScanAnnotationParser、ConditionEvaluator,别离用于包扫描和条件拆卸;
接着调用 ConfigurationClassParser#parse()办法开始解析启动类进行应用程序的启动。
2)解析启动类
以 ConfigurationClassParser#parse()办法为入口,局部代码执行流程如下:
其中在做条件拆卸时,有个点须要留神一下:ConfigurationCondition 接口外部的枚举类 ConfigurationPhase 中有两个值 PARSE_CONFIGURATION、REGISTER_BEAN,别离示意:在类解析阶段做条件拆卸、在类注册阶段做条件拆卸。
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
1> 持续看获取 SourceClass 的代码逻辑:
其中会校验启动类上 @SpringBootApplication 注解的合法性,而后将启动类和其注解元数据 AnnotationMetadata 封装到 SourceClass 中返回。
如果获取不到 SourceClass,则不会执行配置类(启动类)的解决。
2> 获取到 SourceClass 之后,解决配置类和 SourceClass:
ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass,SourceClassPredicate<String>)办法中会解决如下内容:
如果启动类 configClass 被 @Component 的衍生注解(递归注解的父注解能够找到 @Component)标注,则首先递归解决所有成员(嵌套)类:即 configClass 类外部如果找到成员类,会递归调用 doProcessConfigurationClass()办法解决所有成员类。
解析启动类中所有的 @PropertySource、@ComponentScan、@Import、@ImportResource、@Bean 注解。
具体代码如下:
class ConfigurationClassParser {
....
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 启动类 configClass 被 @Component 的衍生注解(递归注解的父注解能够找到 @Component)标注
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// 首先递归解决所有成员(嵌套)类:configClass 类外部如果找到成员类,会递归调用 doProcessConfigurationClass()办法解决所有成员类。processMemberClasses(configClass, sourceClass, filter);
}
// 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");
}
}
// 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)) {
// componentScan 中蕴含 11 个成员,对应于 @ComponentScan 中 11 个属性
for (AnnotationAttributes componentScan : componentScans) {
// 解决 @ComponentScan 中的属性,返回所有派生的 @Component 注解标注的类, 而后立刻进行扫描
// 此处会找到 basePackages,其默认为启动类所在的目录
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
// TODO 理论业务中这里定义了多个派生 @Component 注解标注的类,这里就会循环多少次
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());
}
}
}
}
// Process any @Import annotations
// getImports()办法从启动类中获取所有的 @Import 注解的内容
processImports(configClass, sourceClass, getImports(sourceClass), filter, 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;
}
....
}
就一个最简略、最洁净的 SpringBoot 程序来看,其中没有 @PropertySource、@ImportResource 注解,平时工程中也很少应用。所以本文咱们着重看 @ComponentScan、@Import 两个注解的解决流程(也就是咱们主动拆卸的外围所在)。
3)解析启动类中的 @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)) {
// componentScan 中蕴含 11 个成员,对应于 @ComponentScan 中 11 个属性
for (AnnotationAttributes componentScan : componentScans) {
// 解决 @ComponentScan 中的属性,返回所有派生的 @Component 注解标注的类, 而后立刻进行扫描
// 此处会找到 basePackages,其默认为启动类所在的目录
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
// TODO 理论业务中这里定义了多个派生 @Component 注解标注的类,这里就会循环多少次
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());
}
}
}
}
首先获取启动类 @SpringBootApplication 注解中的 11 个属性,而后调用 ComponentScanAnnotationParser#parse()办法解决 @SpringBootApplication 注解中的注解 并 设置到类门路 BeanDefinition 扫描器 ClassPathBeanDefinitionScanner 的相应属性中。
class ComponentScanAnnotationParser {
....
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);
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");
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 为启动类所在的目录。eg: 启动类为 com.saint.SaintSpringBootApplication,basePackages 为:com.saint
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));
}
}
basePackages 属性为 @ComponentScan 注解的默认扫描包门路,如果没指定该属性,则会将启动类所在的包作为默认值 赋值 basePackages 属性上(以启动类 SaintSpringBootApplication 为例,其默认扫包门路为:com.saint)。
给 ClassPathBeanDefinitionScanner 制订完所有属性之后,会调用其 doScan(String…)办法扫描 basePackages 目录下的所有标注了 @Component 衍生注解(比方:@Controller、@Service、@Repository)的类。
具体代码执行流程如下:
获取并注册完所有的 @Component 衍生类之后,在递归对这些类做解析。
4)解析启动类中的 @Import 注解
在此之前,我聊 SpringBoot 主动拆卸都是说,@EnableAutoConfiguration 注解中通过 @Import 注解导入了 AutoConfigurationImportSelector.class,@EnableAutoConfiguration 注解中的 @AutoConfigurationPackage 中通过 @Import 注解导入了 AutoConfigurationPackages.Registrar.class 类,但我并不知道这里的 @Import 是在何时解决的!!这里咱们就看一下针对 @Import 注解是怎么解决的。
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
1
这里分两步,首先通过 getImports()办法获取启动类中的 @Import 注解,而后再通过 processImports()办法解决所有的 @Import 注解。
1> getImports()办法获取启动类中所有的 @Import 注解:
具体递归流程如下:
最终获取的 @import 注解有两个:
2> processImports()办法解决获取到的启动类中所有的 @Import 注解:
先解决 AutoConfigurationPackages.Registrar.class 类,再解决 AutoConfigurationImportSelector 类;
对 AutoConfigurationPackages.Registrar.class 类的解决比较简单,利用反射将其实例化之后,增加到启动类的 importBeanDefinitionRegistrars 属性中。
因为 AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口,所以会对 AutoConfigurationImportSelector 进行一个解决:将 AutoConfigurationImportSelector 封装为 DeferredImportSelectorHolder 对象,而后增加到 ConfigurationClassParser 类的 deferredImportSelectors 属性中(供前面解决 @Import 内容)。
5)解决所有的主动拆卸类
最初回到 ConfigurationClassParser#parse()办法中:
代码执行流程如下:
最终进入到 AutoConfigurationImportSelector#getAutoConfigurationEntry(AnnotationMetadata)办法,这里的代码逻辑置信大家嘎嘎眼生,在 SpringBoot 主动拆卸机制原理一文中咱们聊过。
AutoConfigurationImportSelector#getAutoConfigurationEntry(AnnotationMetadata)办法源代码如下:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}
// 1. 获取 @EnableAutoConfiguration 标注类的元信息
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 2. 返回主动拆卸类的候选类名汇合
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 3. 移除反复对象,因为 主动拆卸组件存在反复定义的状况
configurations = removeDuplicates(configurations);
// 4. 主动拆卸组件的排除名单
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 5.1. 查看主动拆卸 Class 排除汇合的合法性
checkExcludedClasses(configurations, exclusions);
// 5.2 排除掉不须要主动拆卸的 Class
configurations.removeAll(exclusions);
// 6. 进一步过滤
configurations = getConfigurationClassFilter().filter(configurations);
// 7. 触发主动拆卸的导入事件,事件包含候选的拆卸组件类名单和排除名单。fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
其负责拿到所有主动配置的节点,大抵分为六步;
第一步,在 getCandidateConfigurations()办法中利用 Spring Framework 工厂机制的加载器 SpringFactoriesLoader,通过 SpringFactoriesLoader#loadFactoryNames(Class, ClassLoader)办法读取所有 META-INF/spring.factories 资源中 @EnableAutoConfiguration 所关联的主动拆卸 Class 汇合。
第二步,利用 Set 不可重复性对主动拆卸 Class 汇合进行去重,因为主动拆卸组件存在反复定义的状况;
第三步,读取以后配置类所标注的 @EnableAutoConfiguration 注解的属性 exclude 和 excludeName,并与 spring.autoconfigure.exclude 配置属性的值 合并为主动拆卸 class 排除汇合。
第四步,校验主动拆卸 Class 排除汇合的合法性、并排除掉主动拆卸 Class 排除汇合中的所有 Class(不须要主动拆卸的 Class)。
第五步,再次过滤后候选主动拆卸 Class 汇合中不符合条件拆卸的 Class 成员;
最初一步,触发主动拆卸的导入事件。
phase1> getCandidateConfigurations() –> 获取主动拆卸类:
代码执行流程如下:
getSpringFactoriesLoaderFactoryClass()办法返回咱们相熟的 EnableAutoConfiguration 注解类;
紧接着,SpringFactoriesLoader.loadFactoryNames(Class<?>, ClassLoader)办法会获取所有 META-INF/Spring.factories 的配置文件,进而获取到所有的主动拆卸类;
loadFactoryNames()原理如下:
搜寻指定 ClassLoader 下所有的 META-INF/spring.fatories 资源内容;
将搜寻到的资源内容作为 Properties 文件读取,合并为一个 Key 为接口的全类名、Value 为实现类全类名 列表的 Map,作为办法的返回值;
最初从上一步返回的 Map 中查找并返回办法指定类型 对应的实现类全类名列表。
loadFactoryNames()办法源码解释如下:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 先从缓存中获取
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {return result;}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
/**
* 查找所有咱们依赖的 jar 包,并找到对应有 META-INF/spring.factories ⽂件,而后获取⽂件中的内容
*
* 第一次循环:file:/.../org/springframework/spring-beans/5.2.12.RELEASE/spring-beans-5.2.12.RELEASE.jar!/META-INF/spring.factories
* 第二次循环:file:/.../org/springframework/boot/spring-boot/2.3.7.RELEASE/spring-boot-2.3.7.RELEASE.jar!/META-INF/spring.factories
* 第三次循环:file:/../org/springframework/boot/spring-boot-autoconfigure/2.3.7.RELEASE/spring-boot-autoconfigure-2.3.7.RELEASE.jar!/META-INF/spring.factories
*/
URL url = urls.nextElement();
// 获取资源
UrlResource resource = new UrlResource(url);
// 获取资源的内容
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
phase5> getConfigurationClassFilter().filter(configurations) –> 过滤候选主动拆卸 Class 汇合中不符合条件拆卸的 Class 成员:
这里分两步:
第一步:获取所有的 Filter
首先通过 getConfigurationClassFilter()办法从所有 META-INF/spring.factories 文件中获取所有的主动拆卸过滤器 AutoConfigurationImportFilter 的实现类(一共有三个),而后实例化 AutoConfigurationImportSelector 类的外部类 ConfigurationClassFilter,并将获取多的所有 AutoConfigurationImportFilter 的实现类汇合赋值到 ConfigurationClassFilter 的 filters 属性中(前面会用到它做条件拆卸)。
第二步:执行条件拆卸
而后对获取到的所有主动拆卸类(最洁净、最简略的 SpringBoot 程序有 127 个)执行过滤操作(条件拆卸)后,还剩 23 个主动拆卸类。
对于此处为什么 127 个主动拆卸类通过 AutoConfigurationImportFilter 过滤后只剩 23 个了,且听下回分解《SpringBoot 主动拆卸中的条件拆卸》。
前面就一路返回返回返回!!!!