SpringBoot 系列文章简介
SpringBoot 源码阅读辅助篇:
Spring IoC 容器与应用上下文的设计与实现
SpringBoot 启动流程源码分析:
SpringBoot 启动流程分析(一):SpringApplication 类初始化过程
SpringBoot 启动流程分析(二):SpringApplication 的 run 方法
SpringBoot 启动流程分析 (三):SpringApplication 的 run 方法之 prepareContext() 方法
SpringBoot 启动流程分析(四):IoC 容器的初始化过程
SpringBoot 启动流程分析(五):SpringBoot 自动装配原理实现
SpringBoot 启动流程分析(六):IoC 容器依赖注入
笔者注释版 Spring Framework 与 SpringBoot 源码 Git 传送门: 请不要吝啬小星星
SpringBoot-2.0.4.RELEASE https://github.com/hello-shf/spring-boot-build.git
第五步: 刷新应用上下文
一, 前言
在前面的博客中谈到 IoC 容器的初始化过程, 主要分下面三步:
BeanDefinition 的 Resource 定位
BeanDefinition 的载入
向 IoC 容器注册 BeanDefinition
在上一篇文章介绍了 prepareContext()方法, 在准备刷新阶段做了什么工作. 本文我们主要从 refresh()方法中总结 IoC 容器的初始化过程.
从 run 方法的, refreshContext()方法一路跟下去, 最终来到 AbstractApplicationContext 类的 refresh()方法.
- @Override
- public void refresh() throws BeansException, IllegalStateException {
- synchronized (this.startupShutdownMonitor) {
- // Prepare this context for refreshing.
- // 刷新上下文环境
- prepareRefresh();
- // Tell the subclass to refresh the internal bean factory.
- // 这里是在子类中启动 refreshBeanFactory() 的地方
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- // Prepare the bean factory for use in this context.
- // 准备 bean 工厂, 以便在此上下文中使用
- prepareBeanFactory(beanFactory);
- try {
- // Allows post-processing of the bean factory in context subclasses.
- // 设置 beanFactory 的后置处理
- postProcessBeanFactory(beanFactory);
- // Invoke factory processors registered as beans in the context.
- // 调用 BeanFactory 的后处理器, 这些处理器是在 Bean 定义中向容器注册的
- invokeBeanFactoryPostProcessors(beanFactory);
- // Register bean processors that intercept bean creation.
- // 注册 Bean 的后处理器, 在 Bean 创建过程中调用
- registerBeanPostProcessors(beanFactory);
- // Initialize message source for this context.
- // 对上下文中的消息源进行初始化
- initMessageSource();
- // Initialize event multicaster for this context.
- // 初始化上下文中的事件机制
- initApplicationEventMulticaster();
- // Initialize other special beans in specific context subclasses.
- // 初始化其他特殊的 Bean
- onRefresh();
- // Check for listener beans and register them.
- // 检查监听 Bean 并且将这些监听 Bean 向容器注册
- registerListeners();
- // Instantiate all remaining (non-lazy-init) singletons.
- // 实例化所有的 (non-lazy-init) 单件
- finishBeanFactoryInitialization(beanFactory);
- // Last step: publish corresponding event.
- // 发布容器事件, 结束 Refresh 过程
- finishRefresh();
- } catch (BeansException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Exception encountered during context initialization -" +
- "cancelling refresh attempt:" + ex);
- }
- // Destroy already created singletons to avoid dangling resources.
- destroyBeans();
- // Reset 'active' flag.
- cancelRefresh(ex);
- // Propagate exception to caller.
- throw ex;
- } finally {
- // Reset common introspection caches in Spring's core, since we
- // might not ever need metadata for singleton beans anymore...
- resetCommonCaches();
- }
- }
- }
从以上代码中我们可以看到, refresh()方法中所作的工作也挺多, 我们没办法面面俱到, 主要根据 IoC 容器的初始化步骤和 IoC 依赖注入的过程进行分析, 围绕以上两个过程, 我们主要介绍重要的方法, 其他的请看注释.
二, obtainFreshBeanFactory();
在启动流程的第三步: 初始化应用上下文. 中我们创建了应用的上下文, 并触发了 GenericApplicationContext 类的构造方法如下所示, 创建了 beanFactory, 也就是创建了 DefaultListableBeanFactory 类.
- public GenericApplicationContext() {
- this.beanFactory = new DefaultListableBeanFactory();
- }
关于 obtainFreshBeanFactory()方法, 其实就是拿到我们之前创建的 beanFactory.
- protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
- // 刷新 BeanFactory
- refreshBeanFactory();
- // 获取 beanFactory
- ConfigurableListableBeanFactory beanFactory = getBeanFactory();
- if (logger.isDebugEnabled()) {
- logger.debug("Bean factory for" + getDisplayName() + ":" + beanFactory);
- }
- return beanFactory;
- }
从上面代码可知, 在该方法中主要做了三个工作, 刷新 beanFactory, 获取 beanFactory, 返回 beanFactory.
首先看一下 refreshBeanFactory()方法, 跟下去来到 GenericApplicationContext 类的 refreshBeanFactory()发现也没做什么.
- @Override
- protected final void refreshBeanFactory() throws IllegalStateException {
- if (!this.refreshed.compareAndSet(false, true)) {
- throw new IllegalStateException(
- "GenericApplicationContext does not support multiple refresh attempts: just call'refresh'once");
- }
- this.beanFactory.setSerializationId(getId());
- }
- TIPS:
1,AbstractApplicationContext 类有两个子类实现了 refreshBeanFactory(), 但是在前面第三步初始化上下文的时候,
实例化了 GenericApplicationContext 类, 所以没有进入 AbstractRefreshableApplicationContext 中的 refreshBeanFactory()方法.
2,this.refreshed.compareAndSet(false, true)
这行代码在这里表示: GenericApplicationContext 只允许刷新一次
这行代码, 很重要, 不是在 Spring 中很重要, 而是这行代码本身. 首先看一下 this.refreshed 属性:
private final AtomicBoolean refreshed = new AtomicBoolean();
java J.U.C 并发包中很重要的一个原子类 AtomicBoolean. 通过该类的 compareAndSet()方法可以实现一段代码绝对只实现一次的功能.
感兴趣的自行百度吧.
三, prepareBeanFactory(beanFactory);
从字面意思上可以看出准备 BeanFactory.
看代码, 具体看看做了哪些准备工作. 这个方法不是重点, 看注释吧.
- protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
- // Tell the internal bean factory to use the context's class loader etc.
- // 配置类加载器: 默认使用当前上下文的类加载器
- beanFactory.setBeanClassLoader(getClassLoader());
- // 配置 EL 表达式: 在 Bean 初始化完成, 填充属性的时候会用到
- beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
- // 添加属性编辑器 PropertyEditor
- beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
- // Configure the bean factory with context callbacks.
- // 添加 Bean 的后置处理器
- beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
- // 忽略装配以下指定的类
- beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
- beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
- beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
- beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
- beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
- beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
- // BeanFactory interface not registered as resolvable type in a plain factory.
- // MessageSource registered (and found for autowiring) as a bean.
- // 将以下类注册到 beanFactory(DefaultListableBeanFactory) 的 resolvableDependencies 属性中
- beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
- beanFactory.registerResolvableDependency(ResourceLoader.class, this);
- beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
- beanFactory.registerResolvableDependency(ApplicationContext.class, this);
- // Register early post-processor for detecting inner beans as ApplicationListeners.
- // 将早期后处理器注册为 application 监听器, 用于检测内部 bean
- beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
- // Detect a LoadTimeWeaver and prepare for weaving, if found.
- // 如果当前 BeanFactory 包含 loadTimeWeaver Bean, 说明存在类加载期织入 AspectJ,
- // 则把当前 BeanFactory 交给类加载期 BeanPostProcessor 实现类 LoadTimeWeaverAwareProcessor 来处理,
- // 从而实现类加载期织入 AspectJ 的目的.
- if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
- beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
- // Set a temporary ClassLoader for type matching.
- beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
- }
- // Register default environment beans.
- // 将当前环境变量(environment) 注册为单例 bean
- if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
- beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
- }
- // 将当前系统配置(systemProperties) 注册为单例 Bean
- if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
- beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
- }
- // 将当前系统环境 (systemEnvironment) 注册为单例 Bean
- if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
- beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
- }
- }
四, postProcessBeanFactory(beanFactory);
postProcessBeanFactory()方法向上下文中添加了一系列的 Bean 的后置处理器. 后置处理器工作的时机是在所有的 beanDenifition 加载完成之后, bean 实例化之前执行. 简单来说 Bean 的后置处理器可以修改 BeanDefinition 的属性信息.
关于这个方法就先这样吧, 有兴趣的可以直接百度该方法. 篇幅有限, 对该方法不做过多介绍.
五, invokeBeanFactoryPostProcessors(beanFactory);(重点)
上面说过, IoC 容器的初始化过程包括三个步骤, 在 invokeBeanFactoryPostProcessors()方法中完成了 IoC 容器初始化过程的三个步骤.
1, 第一步: Resource 定位
在 SpringBoot 中, 我们都知道他的包扫描是从主类所在的包开始扫描的, prepareContext()方法中, 会先将主类解析成 BeanDefinition, 然后在 refresh()方法的 invokeBeanFactoryPostProcessors()方法中解析主类的 BeanDefinition 获取 basePackage 的路径. 这样就完成了定位的过程. 其次 SpringBoot 的各种 starter 是通过 SPI 扩展机制实现的自动装配, SpringBoot 的自动装配同样也是在 invokeBeanFactoryPostProcessors()方法中实现的. 还有一种情况, 在 SpringBoot 中有很多的 @EnableXXX 注解, 细心点进去看的应该就知道其底层是 @Import 注解, 在 invokeBeanFactoryPostProcessors()方法中也实现了对该注解指定的配置类的定位加载.
常规的在 SpringBoot 中有三种实现定位, 第一个是主类所在包的, 第二个是 SPI 扩展机制实现的自动装配(比如各种 starter), 第三种就是 @Import 注解指定的类.(对于非常规的不说了)
2, 第二步: BeanDefinition 的载入
在第一步中说了三种 Resource 的定位情况, 定位后紧接着就是 BeanDefinition 的分别载入. 所谓的载入就是通过上面的定位得到的 basePackage,SpringBoot 会将该路径拼接成: classpath*:org/springframework/boot/demo/**/*.class 这样的形式, 然后一个叫做 PathMatchingResourcePatternResolver 的类会将该路径下所有的. class 文件都加载进来, 然后遍历判断是不是有 @Component 注解, 如果有的话, 就是我们要装载的 BeanDefinition. 大致过程就是这样的了.
TIPS:
@Configuration,@Controller,@Service 等注解底层都是 @Component 注解, 只不过包装了一层罢了.
3, 第三个过程: 注册 BeanDefinition
这个过程通过调用上文提到的 BeanDefinitionRegister 接口的实现来完成. 这个注册过程把载入过程中解析得到的 BeanDefinition 向 IoC 容器进行注册. 通过上文的分析, 我们可以看到, 在 IoC 容器中将 BeanDefinition 注入到一个 ConcurrentHashMap 中, IoC 容器就是通过这个 HashMap 来持有这些 BeanDefinition 数据的. 比如 DefaultListableBeanFactory 中的 beanDefinitionMap 属性.
OK, 总结完了, 接下来我们通过代码看看具体是怎么实现的.
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
...
}
// PostProcessorRegistrationDelegate 类
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
...
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
...
}
// PostProcessorRegistrationDelegate 类
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
// ConfigurationClassPostProcessor 类
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
...
processConfigBeanDefinitions(registry);
}
// ConfigurationClassPostProcessor 类
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
...
do {
parser.parse(candidates);
parser.validate();
...
}
...
}
一路跟踪调用栈, 来到 ConfigurationClassParser 类的 parse()方法.
// ConfigurationClassParser 类
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 如果是 SpringBoot 项目进来的, bd 其实就是前面主类封装成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition 接口的实现类)
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);
}
}
// 加载默认的配置 ---》(对 springboot 项目来说这里就是自动装配的入口了)
processDeferredImportSelectors();
}
看上面的注释, 在前面的 prepareContext()方法中, 我们详细介绍了我们的主类是如何一步步的封装成 AnnotatedGenericBeanDefinition, 并注册进 IoC 容器的 beanDefinitionMap 中的.
TIPS:
至于 processDeferredImportSelectors(); 方法, 后面我们分析 SpringBoot 的自动装配的时候会详细讲解, 各种 starter 是如何一步步的实现自动装配的.<SpringBoot 启动流程分析(五):SpringBoot 自动装配原理实现>
继续沿着 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); 方法跟下去
// ConfigurationClassParser 类
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
// ConfigurationClassParser 类
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
...
// Recursively process the configuration class and its superclass hierarchy.
// 递归地处理配置类及其父类层次结构.
SourceClass sourceClass = asSourceClass(configClass);
do {
// 递归处理 Bean, 如果有父类, 递归处理, 直到顶层父类
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
// ConfigurationClassParser 类
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
// 首先递归处理内部类,(SpringBoot 项目的主类一般没有内部类)
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
// 针对 @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.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// 根据 @ComponentScan 注解, 扫描项目中的 Bean(SpringBoot 启动类上有该注解)
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
// 立即执行扫描,(SpringBoot 项目为什么是从主类所在的包扫描, 这就是关键了)
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();
}
// 检查是否是 ConfigurationClass(是否有 configuration/component 两个注解), 如果是, 递归查找该类相关联的配置类.
// 所谓相关的配置类, 比如 @Configuration 中的 @Bean 定义的 bean. 或者在有 @Component 注解的类上继续存在 @Import 注解.
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
// 递归处理 @Import 注解(SpringBoot 项目中经常用的各种 @Enable*** 注解基本都是封装的 @Import)
processImports(configClass, sourceClass, getImports(sourceClass), 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;
}
看 doProcessConfigurationClass()方法.(SpringBoot 的包扫描的入口方法, 重点哦)
我们先大致说一下这个方法里面都干了什么, 然后稍后再阅读源码分析.
TIPS:
在以上代码的第 60 行 parse(bdCand.getBeanClassName(), holder.getBeanName()); 会进行递归调用,
因为当 Spring 扫描到需要加载的类会进一步判断每一个类是否满足是 @Component/@Configuration 注解的类,
如果满足会递归调用 parse()方法, 查找其相关的类.
同样的第 68 行 processImports(configClass, sourceClass, getImports(sourceClass), true);
通过 @Import 注解查找到的类同样也会递归查找其相关的类.
两个递归在 debug 的时候会很乱, 用文字叙述起来更让人难以理解, 所以, 我们只关注对主类的解析, 及其类的扫描过程.
上面代码的第 29 行 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(... 获取主类上的 @PropertySource 注解(关于该注解是怎么用的请自行百度), 解析该注解并将该注解指定的 properties 配置文件中的值存储到 Spring 的 Environment 中, Environment 接口提供方法去读取配置文件中的值, 参数是 properties 文件中定义的 key 值.
42 行 Set<AnnotationAttributes> componentScans =AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 解析主类上的 @ComponentScan 注解, 呃, 怎么说呢, 42 行后面的代码将会解析该注解并进行包扫描.
68 行 processImports(configClass, sourceClass, getImports(sourceClass), true); 解析主类上的 @Import 注解, 并加载该注解指定的配置类.
TIPS:
在 spring 中好多注解都是一层一层封装的, 比如 @EnableXXX, 是对 @Import 注解的二次封装.@SpringBootApplication 注解 =@ComponentScan+@EnableAutoConfiguration+@Import+@Configuration+@Component.@Controller,@Service 等等是对 @Component 的二次封装...
5.1, 看看 42-64 行干了啥
从上面的 42 行往下看, 来到第 49 行 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
进入该方法
// ComponentScanAnnotationParser 类
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
...
// 根据 declaringClass (如果是 SpringBoot 项目, 则参数为主类的全路径名)
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
...
// 根据 basePackages 扫描类
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
发现有两行重要的代码
为了验证代码中的注释, debug, 看一下 declaringClass, 如下图所示确实是我们的主类的全路径名.
跳过这一行, 继续 debug, 查看 basePackages, 该 set 集合中只有一个, 就是主类所在的路径.
TIPS:
为什么只有一个还要用一个集合呢, 因为我们也可以用 @ComponentScan 注解指定扫描路径.
到这里呢 IoC 容器初始化三个步骤的第一步, Resource 定位就完成了, 成功定位到了主类所在的包.
接着往下看 return scanner.doScan(StringUtils.toStringArray(basePackages)); Spring 是如何进行类扫描的. 进入 doScan()方法.
// ComponentScanAnnotationParser 类
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) {
// 从指定的包中扫描需要装载的 Bean
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
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);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 将该 Bean 注册进 IoC 容器(beanDefinitionMap)
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
这个方法中有两个比较重要的方法, 第 7 行 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 从 basePackage 中扫描类并解析成 BeanDefinition, 拿到所有符合条件的类后在第 24 行 registerBeanDefinition(definitionHolder, this.registry); 将该类注册进 IoC 容器. 也就是说在这个方法中完成了 IoC 容器初始化过程的第二三步, BeanDefinition 的载入, 和 BeanDefinition 的注册.
5.1.1,findCandidateComponents(basePackage);
跟踪调用栈
// ClassPathScanningCandidateComponentProvider 类
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
...
else {
return scanCandidateComponents(basePackage);
}
}
// ClassPathScanningCandidateComponentProvider 类
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 拼接扫描路径, 比如: classpath*:org/springframework/boot/demo/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// 从 packageSearchPath 路径中扫描所有的类
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning" + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// // 判断该类是不是 @Component 注解标注的类, 并且不是需要排除掉的类
if (isCandidateComponent(metadataReader)) {
// 将该类封装成 ScannedGenericBeanDefinition(BeanDefinition 接口的实现类)类
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class:" + resource);
}
candidates.add(sbd);
} else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class:" + resource);
}
}
} else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter:" + resource);
}
}
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class:" + resource, ex);
}
} else {
if (traceEnabled) {
logger.trace("Ignored because not readable:" + resource);
}
}
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
在第 13 行将 basePackage 拼接成 classpath*:org/springframework/boot/demo/**/*.class, 在第 16 行的 getResources(packageSearchPath); 方法中扫描到了该路径下的所有的类. 然后遍历这些 Resources, 在第 27 行判断该类是不是 @Component 注解标注的类, 并且不是需要排除掉的类. 在第 29 行将扫描到的类, 解析成 ScannedGenericBeanDefinition, 该类是 BeanDefinition 接口的实现类. OK,IoC 容器的 BeanDefinition 载入到这里就结束了.
回到前面的 doScan()方法, debug 看一下结果(截图中所示的就是我定位的需要交给 Spring 容器管理的类).
5.1.2,registerBeanDefinition(definitionHolder, this.registry);
查看 registerBeanDefinition()方法. 是不是有点眼熟, 在前面介绍 prepareContext()方法时, 我们详细介绍了主类的 BeanDefinition 是怎么一步一步的注册进 DefaultListableBeanFactory 的 beanDefinitionMap 中的. 在此呢我们就省略 1w 字吧. 完成了 BeanDefinition 的注册, 就完成了 IoC 容器的初始化过程. 此时, 在使用的 IoC 容器 DefaultListableFactory 中已经建立了整个 Bean 的配置信息, 而这些 BeanDefinition 已经可以被容器使用了. 他们都在 BeanbefinitionMap 里被检索和使用. 容器的作用就是对这些信息进行处理和维护. 这些信息是容器简历依赖反转的基础.
- protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
- BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
- }
OK, 到这里 IoC 容器的初始化过程的三个步骤就梳理完了. 当然这只是针对 SpringBoot 的包扫描的定位方式的 BeanDefinition 的定位, 加载, 和注册过程. 前面我们说过, 还有两种方式 @Import 和 SPI 扩展实现的 starter 的自动装配.
5.2,@Import 注解的解析过程
相信不说大家也应该知道了, 各种 @EnableXXX 注解, 很大一部分都是对 @Import 的二次封装(其实也是为了解耦, 比如当 @Import 导入的类发生变化时, 我们的业务系统也不需要改任何代码).
呃, 我们又要回到上文中的 ConfigurationClassParser 类的 doProcessConfigurationClass 方法的第 68 行 processImports(configClass, sourceClass, getImports(sourceClass), true);, 跳跃性比较大. 上面解释过, 我们只针对主类进行分析, 因为这里有递归.
processImports(configClass, sourceClass, getImports(sourceClass), true); 中 configClass 和 sourceClass 参数都是主类相对应的哦.
TIPS:
在分析这一块的时候, 我在主类上加了 @EnableCaching 注解.
首先看 getImports(sourceClass);
- private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
- Set<SourceClass> imports = new LinkedHashSet<>();
- Set<SourceClass> visited = new LinkedHashSet<>();
- collectImports(sourceClass, imports, visited);
- return imports;
- }
- debug
正是 @EnableCaching 注解中的 @Import 注解指定的类. 另外两个呢是主类上的 @SpringBootApplication 中的 @Import 注解指定的类. 不信你可以一层层的剥开 @SpringBootApplication 注解的皮去一探究竟.
至于 processImports()方法, 大家自行 debug 吧, 相信看到这里, 思路大家都已经很清楚了.
凌晨两点了, 睡觉, 明天继续上班.
来源: https://www.cnblogs.com/hello-shf/p/11051476.html