Spring Annotation 启动流程
本文将对 Spring 注解方式的启动流程进行分析 author: huifer
实例
- 在开始分析启动流程之前我们需要先编写一个例子,这个例子是我们分析源码的根.
 
下面是基本用例
package org.source.hot.spring.overview.ioc.bean.annotation;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
 *
 *
 * @author huifer
 */
public class AnnotationContextDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext(AnnotationContextDemo.class);
        Us bean = context.getBean(Us.class);
        context.close();
    }
    @Bean
    public Us us() {
        Us us = new Us();
        us.setName("a");
        return us;
    }
}
class Us {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
对于这个类我们首先需要关注的是
AnnotationConfigApplicationContext构造函数AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationContextDemo.class);其次我们需要对
@Bean注解进行分析
AnnotationConfigApplicationContext 构造函数分析-参数是Class列表
首先我们来对
AnnotationConfigApplicationContext构造函数进行分析下面代码是我们在
AnnotationContextDemo中编写的构造函数的详细代码.public AnnotationConfigApplicationContext(Class<?>... componentClasses) { this(); register(componentClasses); refresh(); }
三行代码分别做了什么呢?
this():this()的完整代码 👇public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
赋值成员变量
reader和scanner/** * 注解版本的beanDefinition阅读器 */ private final AnnotatedBeanDefinitionReader reader; /** * 扫描器 */ private final ClassPathBeanDefinitionScanner scanner;register(componentClasses)注册组件
注册逐渐依靠的是成员变量
reader中的register方法refresh()刷新上下文
方法提供者:
AbstractApplicationContext(本文不做分析)
- 在构造函数中我们可以明确需要分析的方法是
register,即org.springframework.context.annotation.AnnotatedBeanDefinitionReader#register 
AnnotatedBeanDefinitionReader#register
- 注解模式下bean定义的注册方法
 
入口方法代码如下
public void register(Class<?>... componentClasses) {
   for (Class<?> componentClass : componentClasses) {
      registerBean(componentClass);
   }
}
这里可以看出参数是多个组件类列表, 根据前文实例我们的参数是: org.source.hot.spring.overview.ioc.bean.annotation.AnnotationContextDemo
整体代码逻辑就是循环注册组件
真正需要关注的方法在doRegisterBean 中
首先我们来看参数
Class<T> beanClass: bean 类型String name: bean 名称@Nullable Class<? extends Annotation>[] qualifiers: 限定注解@Nullable Supplier<T> supplier: bean 实例提供者@Nullable BeanDefinitionCustomizer[] customizers: 自定义处理Bean定义的实现类列表
看完参数我们来整理执行流程
创建
AnnotatedGenericBeanDefinition(带有注解的泛型bean定义)在
AnnotatedGenericBeanDefinition中存储了关于 Bean的相关信息这里举几个例子
beanClasslazyInitprimary- ....
 
有兴趣的可以查阅下面类图中的
AnnotatedGenericBeanDefinition、GenericBeanDefinition、AbstractBeanDefinition所包含的成员变量
设置实例提供者
设置实例提供者就是将参数
@Nullable Supplier<T> supplier设置到AnnotatedGenericBeanDefinition成员变量中解析Scope属性,并设置
解析Scope属性依靠
ScopeMetadataResolver接口, 在注解环境下一般是AnnotationScopeMetadataResolver实现类beanName 处理
beanName的处理其实也是根据参数
String name进行的当参数
name存在的情况下就用参数的name作为 beanName当参数
name不存在的情况下会依靠 **BeanNameGenerator接口 ** 生成名称, 在注解环境下一般是AnnotationBeanNameGenerator提供具体的实现方法beanName 的可能:
- 从 
value属性中获取.value属性会在@Bean、@Service、@Component等中出现 - 短类名,首字母小写
 
- 从 
 通用注解处理
在这一步会对通用注解进行处理, 即设置
AnnotatedGenericBeanDefinition中部分成员变量的属性通用注解:
@Lazy@DependsOn@Role@Description
- 方法提供者: 
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd) 
参数
@Nullable BeanDefinitionCustomizer[] customizers的处理, 即对bean定义的自定义处理这一段就是一个循环调用
BeanDefinitionCustomizer#customize注册bean定义
完整代码如下
private
void doRegisterBean(Class beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier supplier, @Nullable BeanDefinitionCustomizer[] customizers) { // 带有注解的泛型bean定义 AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); // 和条件注解相关的函数 if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } // 设置实例提供者 abd.setInstanceSupplier(supplier); // 解析 注解的 beanDefinition 的作用域元数据 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); // 设置 作用域元数据 abd.setScope(scopeMetadata.getScopeName()); // beanName 处理 String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
// 通用注解的处理 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); if (qualifiers != null) { for (Class<? extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } // 自定义的beanDefinition处理 if (customizers != null) { for (BeanDefinitionCustomizer customizer : customizers) { customizer.customize(abd); } }
// 创建 beanDefinition Holder 后进行注册 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); // 应用作用域代理 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
关于
refresh的分析各位可以在这个项目中找到👉Spring-Analysis这是我们关于参数是多个class的构造函数分析, 在
AnnotationConfigApplicationContext还提供了字符串形式的扫描.这也是后续SpringBoot中关于扫描的核心. 下面我们来看看扫描的方法构造函数如下
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }
真正关注的方法应该时
scan@Override public void scan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); this.scanner.scan(basePackages); }
scan方法依靠ClassPathBeanDefinitionScanner类所提供的scan方法, 找到了目标方法下面开始对其进行分析。
在 scan 方法中的执行流程如下
- 获取未进行注册前的bean数量
 - 扫描包路径,进行注册
 - 注册 注解的配置处理器
 - 得到注册的bean数量
 
在这段流程中核心方法是第二步(doScan) ,先回顾一下scan方法的实现代码
public int scan(String... basePackages) {
   // 在执行扫描方法前beanDefinition的数量
   int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
   // 真正的扫描方法
   doScan(basePackages);
   // 是否需要注册 注解的配置处理器
   // Register annotation config processors, if necessary.
   if (this.includeAnnotationConfig) {
      // 注册注解后置处理器
      AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
   }
   //  当前 BeanDefinition 数量 - 历史 B
   return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
- 下面将展开
doScan方法的分析 
处理流程
循环每个包路径
在指定包路径中找到可能的组件(处理方法:
findCandidateComponents)什么是可能的组件?
在Spring中对可能组件的判断代码如下
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { // 从 注解的bean定义中获取注解元信息 AnnotationMetadata metadata = beanDefinition.getMetadata(); // 1. 是否独立 // 2. 是否可以创建 // 3. 是否 abstract 修饰 // 4. 是否有 Lookup 注解 return (metadata.isIndependent() && (metadata.isConcrete() || (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName())))); }扫描方法各位请查看
addCandidateComponentsFromIndex scanCandidateComponents
在得到可能的组件列表后,注意这里可能的组件列表就是 BeanDefinition , 后续的操作就是围绕 BeanDefinition 进行
设置作用域
beanName处理
bean定义的后置处理
处理内容如下
- 设置默认值, 默认值从
BeanDefinitionDefaults中获取 - 设置 
autowireCandidate 
- 设置默认值, 默认值从
 通用注解处理
beanName和候选对象的匹配检测
beanName 是否存在
容器中BeanName对应的实例和参数传递的BeanDefinition是否兼容
Spring 中对于兼容的判断
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) { // 1. 是否是 ScannedGenericBeanDefinition 类型 // 2. source 是否相同 // 3. 参数是否相同 return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean (newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) || // scanned same file twice newDefinition.equals(existingDefinition)); // scanned equivalent class twice }如果通过了匹配检测则加入到容器
在
findCandidateComponents方法中没有展开addCandidateComponentsFromIndex和scanCandidateComponents方法 只是将判断可能组件的方式提了出来, 更多细节各位读者还需要自行查看下面是Spring中关于
doScan的完整代码protected Set
doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); // bean 定义持有器列表 Set beanDefinitions = new LinkedHashSet<>(); // 循环包路径进行扫描 for (String basePackage : basePackages) { // 搜索可能的组件. 得到 组件的BeanDefinition Set candidates = findCandidateComponents(basePackage); // 循环候选bean定义 for (BeanDefinition candidate : candidates) { // 获取 作用域元数据 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); // 设置作用域 candidate.setScope(scopeMetadata.getScopeName()); // beanName 生成 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 类型判断 AbstractBeanDefinition if (candidate instanceof AbstractBeanDefinition) { // bean 定义的后置处理 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } // 类型判断 AnnotatedBeanDefinition 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定义 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; } 到这关于Spring注解模式的启动方式全部分析完成. 感谢各位的阅读
 
 
 
 
 
 