在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 {
....@Nullableprotected 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 排除掉不须要主动拆卸的Classconfigurations.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主动拆卸中的条件拆卸》。
前面就一路返回返回返回!!!!