Spring IOC 容器預啟動流程原始碼探析

雕爺發表於2020-10-12

Spring IOC 容器預啟動流程原始碼探析

在應用程式中,一般是通過建立ClassPathXmlApplicationContextAnnotationConfigApplicationContext這兩個最底層子類來啟動Spring IOC容器:

  • ClassPathXmlApplicationContext: xml檔案配置版
  • AnnotationConfigApplicationContext: 註解版

由於當下越來越流行基於Java註解的配置來建立我們的Bean,所以本文主要以註解版進行探析。

AnnotationConfigApplicationContext的類關係結構

我們先來看看我們探討的起點

public class Main {

	public static void main(String[] args) {
		new AnnotationConfigApplicationContext(Config.class);
	}
	@Configuration
	public static class Config{

	}
}

demo簡簡單單,那麼,這裡發生了什麼?或許,我們可以先看看AnnotationConfigApplicationContext的類關係結構:

在這裡插入圖片描述

我們可以看到AnnotationConfigApplicationContext最上面有兩個頂級介面:

  • BeanFactory: Spring的核心介面,純粹的bean容器,主要定義了與Bean的相關方法
  • ResourceLoader:資源載入器,定義了getResource方法

繼承自三個父類:

  • DefaultResourceLoader: 預設的資源載入器,實現了三種載入資源的方式

    1. 通過path載入資源

    2. 通過classpath載入資源

    3. 通過URL載入資源

  • AbstractApplicationContext: 實現了ApplicationContext介面的抽象類,主要功能

    1. 實現了啟動IOC容器的核心方法:refresh()

    2. 釋出事件

    3. 大量getBean相關的操作, 主要通過抽象方法getBeanFactory基於子類實現

    4. 大量留於子類擴充套件的空方法

    5. 訊息國際化

  • GenericApplicationContext:

    1. 使用組合的方式引進了最底層的BeanFactory實現類:DefaultListableBeanFactory

    2. 定義了registerBean的相關操作,其實是通過DefaultListableBeanFactory實現的

不難發現,ApplicationContext名副其實,確實就是一個應用上下文,對於bean的相關操作,容器的管理,依舊是由我們的BeanFactory進行實現。

準備啟動

1. 建立我們的例項:AnnotationConfigApplicationContext

new AnnotationConfigApplicationContext(Config.class);

2.進入到AnnotationConfigApplicationContext構造方法

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    register(annotatedClasses);
    refresh();
}

3. 呼叫我們的空構造方法,這裡要先例項化我們的父類

3.1 例項化DefaultResourceLoader

public DefaultResourceLoader() {
	this.classLoader = ClassUtils.getDefaultClassLoader();
}

ClassUtils.getDefaultClassLoader()主要有兩步操作

//獲取執行緒上下文的類載入器
ClassLoader cl = = Thread.currentThread().getContextClassLoader();
if(cl == null) //為空則獲取系統的類載入器 即為應用類載入器
    cl = ClassLoader.getSystemClassLoader();

這裡我們非Tomcat環境,所以返回的是AppClassLoader

3.2 例項化AbstractApplicationContext

//為BeanFactoryPostProcessor賦初始值
List<BeanFactoryPostProcessor> BeanFactoryPostProcessor = new ArrayList<>();
public AbstractApplicationContext() {
    //引入一個資源解析器
    this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
    return new PathMatchingResourcePatternResolver(this);
}

3.3 例項化GenericApplicationContext

public GenericApplicationContext() {
    //引入BeanFactory實現
    this.beanFactory = new DefaultListableBeanFactory();
}

3.4 例項化自己

public AnnotationConfigApplicationContext() {
    //初始化基於註解的bean定義掃描器
    this.reader = new AnnotatedBeanDefinitionReader(this);
    //初始化基於classpath的bean定義掃描器
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}
3.4.1 AnnotatedBeanDefinitionReader初始化過程
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    //registry就是我們AnnotationConfigApplicationContext
    this.registry = registry;
    //引入條件表示式計算器 處理@Conditional註解
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    //註冊所有與註解相關的後置處理器
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
	registerAnnotationConfigProcessors(registry, null);
}

registerAnnotationConfigProcessors(registry, null)中主要做了以下幾件事情:

  • DefaultListableBeanFactory賦值了兩個引用

    //依賴排序器,用於處理新增了Priority、Order註解以及實現了Ordered介面的bean
    beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
    //@Autowire候選解析器
    beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
    
  • 往容器中註冊了6個後置處理器的bean定義

    註冊配置類的後置處理器

    RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    def.setSource(source);
    registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME);
    

    註冊處理@Autowired註解的後置處理器

    RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME);
    

    註冊處理@Required註解的後置處理器(5.1版本開始已被廢棄)

    RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME);
    

    註冊處理JSR-250規範註解的後置處理器,@Resource,@PostConstruct,@PreDestroy

    RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
    def.setSource(source);
    registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME);
    

    註冊處理@EventListener註解的後置處理器

    RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
    def.setSource(source);
    registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME);
    

    註冊事件監聽工廠,給上面的EventListenerMethodProcessors使用

    RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
    def.setSource(source);
    registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME);
    
3.4.2 ClassPathBeanDefinitionScanner初始化過程

經歷了一系列的構造器傳遞

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
    this(registry, true);
}

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
    this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
}

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                                      Environment environment) {
    this(registry, useDefaultFilters, environment,
         (registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
}

最終實現的構造器方法

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                                      Environment environment, @Nullable ResourceLoader resourceLoader) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
	//預設為true
    if (useDefaultFilters) {
        //註冊預設的過濾器
        registerDefaultFilters();
    }
    //設定環境
    setEnvironment(environment);
    //設定資源載入器
    setResourceLoader(resourceLoader);
}

registerDefaultFilters方法

protected void registerDefaultFilters() {
    //加入掃描@Component註解的過濾器,這樣就能掃到@Controller,@Service...
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        //JSR-250規範的註解
        this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
    }
    catch (ClassNotFoundException ex) {
    }
    try {
        //JSR-330規範的註解
        this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
    }
    catch (ClassNotFoundException ex) {
    }
}

4. 構造方法執行完畢,執行register(annotatedClasses)方法,將配置類的bean定義註冊到容器中

public void register(Class<?>... annotatedClasses) {
    Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
    //這裡就使用了剛剛初始化的AnnotatedBeanDefinitionReader掃碼器
    //annotatedClasses即為在入口處傳進的自定義配置類Config.class
    this.reader.register(annotatedClasses);
}
public void register(Class<?>... annotatedClasses) {
    for (Class<?> annotatedClass : annotatedClasses) {
        //這裡我們只傳了一個,只有一次迴圈
        registerBean(annotatedClass);
    }
}
public void registerBean(Class<?> annotatedClass) {
    //spring的特點,真正實現的都是do開頭的方法
    doRegisterBean(annotatedClass, null, null, null);
}
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
                        @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
    //將class封裝到bean定義中
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    //由於配置類並未使用@Conditional註解,直接返回false
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        return;
    }

    abd.setInstanceSupplier(instanceSupplier);
    //解析bean定義的作用域
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    //處理普通的bean定義註解,@Lazy @Primary @DependsOn @Role @Description
    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));
            }
        }
    }
    for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
        customizer.customize(abd);
    }

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    //根據scopeMetadata中的proxy-mode屬性判斷是否需要進行代理封裝,預設否
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    //將bean定義註冊到容器中
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
} 

前期準備工作已基本完畢,可以開始呼叫refresh方法啟動IOC容器了。

準備花個30天時間,系統的來整理一下我對spring原始碼的認識:

Spring 原始碼系列
  1. Spring原始碼分析之 IOC 容器預啟動流程(已完結)
  2. Spring原始碼分析之BeanFactory體系結構(已完結)
  3. Spring原始碼分析之BeanFactoryPostProcessor呼叫過程(已完結)
  4. Spring原始碼分析之Bean的建立過程
  5. Spring原始碼分析之什麼是迴圈依賴及解決方案
  6. Spring原始碼分析之AOP從解析到呼叫
  7. Spring原始碼分析之事務管理(上),事物管理是spring作為容器的一個特點,總結一下他的基本實現與原理吧
  8. Spring原始碼分析之事務管理(下) ,關於他的底層事物隔離與事物傳播原理,重點分析一下
Spring Mvc 原始碼系列
  1. SpringMvc體系結構
  2. SpringMvc原始碼分析之Handler解析過程
  3. SpringMvc原始碼分析之請求鏈過程
Mybatis 原始碼系列

暫定


追更,可關注我的公眾號:奇客時間,分享純粹為了樂趣,也有一種成就感吧,筆者這篇文章先就到這

相關文章