1. 程式人生 > >Spring(三)核心容器 - ApplicationContext 上下文啟動準備

Spring(三)核心容器 - ApplicationContext 上下文啟動準備

目錄

  • 前言
  • 正文
    • 第一步:prepareRefresh
    • 第二步:obtainFreshBeanFactory
    • 第三步:prepareBeanFactory
    • 第四步:postProcessBeanFactory
  • 總結

前言

前面介紹了 Spring 容器的概念,其核心可歸納為兩個類: BeanFactory 和 ApplicationContext,ApplicationContext 繼承自 BeanFactory ,其不僅包含 BeanFactory 所有功能,還擴充套件了容器功能。之後介紹了在 SSM 時期和 SpringBoot 時期如何啟動 ApplicationContext 。在結尾處,我們指出,ApplicationContext 核心其實是 refresh 方法,容器一系列功能都在該方法中實現,如:註冊 Bean、注入 Bean 等。

在 refresh 方法中,實現容器核心功能前,先進行了一系列環境準備工作,我們以 SpringBoot 為當前執行環境,深入討論這部分內容。

注:本篇文章使用的 SpringBoot 版本為 2.0.3.RELEASE,其 Spring 版本為 5.0.7.RELEASE

正文

refresh 方法定義在 ConfigurableApplicationContext 介面中,被 AbstractApplicationContext 抽象類實現,該方法由十幾個子方法組成,這些子方法各司其職,但部分子方法被 AbstractApplicationContext 的子類進行擴充套件,來增強功能。其中,前四個子方法主要進行上下文準備工作。

第一步:prepareRefresh

我們先從 refresh 中的 prepareRefresh 方法開始討論:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 初始化上下文環境,就是記錄下容器的啟動時間、活動狀態等
        prepareRefresh();

        ...
    }
}

該方法被繼承 AbstractApplicationContext 抽象類的子類進行擴充套件,擴充套件該方法的子類有:

因本次演示的環境是 SpringBoot ,前面我們講過,SpringBoot 會根據當前 Web 應用型別建立不同的上下文物件 ,如 Servlet Web、Reactive Web 等。這裡演示的是 Servlet Web 應用,所以建立的上下文物件是 AnnotationConfigServletWebServerApplicationContext 。該類的 prepareRefresh 方法會被執行:

public class AnnotationConfigServletWebServerApplicationContext
        extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {

    ...
    
    @Override
    protected void prepareRefresh() {
    
        // 清除 Class 的元資料快取。底層用 Map 儲存元資料,執行 Map 的 clear 方法
        this.scanner.clearCache();
        
        // 呼叫父類,也就是 AbstractApplicationContext 的 prepareRefresh 方法
        super.prepareRefresh();
    }
    ...     
}
public abstract class AbstractApplicationContext {
    
    ...
    
    private long startupDate;
    
    private final AtomicBoolean active = new AtomicBoolean();

    private final AtomicBoolean closed = new AtomicBoolean();

    private Set<ApplicationEvent> earlyApplicationEvents;

    ...

    protected void prepareRefresh() {
        
        // 記錄此上下文開始時的系統時間(以毫秒為單位)
        this.startupDate = System.currentTimeMillis();
        
        // 記錄此上下文是否已關閉,這裡設定為未關閉
        this.closed.set(false);
        
        // 記錄此上下文是否處於活動狀態,這裡設定為活動狀態
        this.active.set(true);
    
        if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        }
    
        // 這也是交由子類擴充套件的方法。具體子類為 GenericWebApplicationContext,主要是初始化屬性源,
        // 將 ServletContext 和 ServletConfig 屬性配置新增到 Environment 環境上下文中
        initPropertySources();
    
        // 校驗 Environment 中那些必備的屬性配置是否存在,不存在則拋異常。
        getEnvironment().validateRequiredProperties();
    
        // 建立 ApplicationEvent 事件集合
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

}

refresh 中的 prepareRefresh 方法執行結束,主要是記錄容器的啟動時間、活動狀態、檢查必備屬性是否存在。其中,關於 Environment 環境配置,在前面 SpringBoot 系列文章中有詳細討論,感興趣的同學可自行翻閱。

第二步:obtainFreshBeanFactory

接著進入 refresh 中的 obtainFreshBeanFactory 方法

public abstract class AbstractApplicationContext {
    
    ...
    
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            
            ...
            
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            ...
        }
    }
    ...
    
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 該方法也是由子類擴充套件,其子類有 AbstractRefreshableApplicationContext 和 GenericApplicationContext,
        // 因當前是 Servlet Web 應用,所以執行的是 GenericApplicationContext 中的 refreshBeanFactory 方法。
        // 該方法主要設定 BeanFactory 的 serializationId 屬性值,也就是序列化id
        refreshBeanFactory();
        
        // 通過 getBeanFactory 返回 BeanFactory 物件。同樣也是由子類擴充套件,呼叫的是 GenericApplicationContext 類中的 getBeanFactory 方法。
        // 返回的是 DefaultListableBeanFactory 。
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }
    
    ...
}

obtainFreshBeanFactory 方法很簡單,但如果當前是非 Servlet Web 應用,執行的就是 AbstractRefreshableApplicationContext 中的 refreshBeanFactory 方法,那可就複雜多了,這裡就不展開討論。之後,該方法還返回了 BeanFactory 物件,從這也可以看出 ApplicationContext 底層是以 BeanFactory 為基礎,逐步擴充套件 Spring 容器功能。

第三步:prepareBeanFactory

接著進入 refresh 中的 prepareBeanFactory 方法。prepareBeanFactory 方法主要是對 BeanFactory 做一些配置,包含各種類載入器、需要忽略的依賴以及後置處理器、解析器等,

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
            
        ...
        
        prepareBeanFactory(beanFactory);
        ... 
    }
    ...
}

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 設定類載入器
    beanFactory.setBeanClassLoader(getClassLoader());
    // 設定表示式解析器,主要用來解析 EL 表示式; Bean 初始化完成後填充屬性時會用到
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    // 設定屬性註冊解析器,主要用來解析 Bean 中的各種屬性型別,如 String、int 等
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    // 新增一個後置處理器:ApplicationContextAwareProcessor。
    // 該後置處理器用於向實現了 Aware 系列介面的 bean 設定相應屬性。
    // (後置處理器和 Aware 介面也是比較核心的概念,後面會有文章詳細討論)
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    // 以下介面,在自動注入時會被忽略,其都是 Aware 系列介面
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

    // 當以下特殊的 Bean 需自動注入時,指定其注入的型別 。
    // 如:注入 BeanFactory 時,注入的型別物件為 ConfigurableListableBeanFactory 。
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // 新增 ApplicationListenerDetector 後置處理器。
    // 該後置處理器用來檢測那些實現了 ApplicationListener 介面的 bean,並將其新增到應用上下文的事件廣播器上。
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // 判斷容器中是否存在 loadTimeWeaver Bean,如果存在則上下文使用臨時的 ClassLoader 進行型別匹配。
    // 整合 AspectJ 時會用到 loadTimeWeaver 物件。
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 註冊和環境相關的 Bean,如 environment、systemProperties、systemEnvironment
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

在 prepareBeanFactory 方法中,主要對 BeanFactory 添加了一系列屬性項,如新增忽略自動注入的介面、新增 BeanPostProcessor 後置處理器、手動註冊部分特殊的 Bean及環境相關的 Bean 。

第四步:postProcessBeanFactory

postProcessBeanFactory 方法是上下文準備的最後一步,主要用來註冊 Web 請求相關的處理器、Bean及配置。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        
        ...
        
        postProcessBeanFactory(beanFactory);
        ...   
    }
}

該方法也是由子類進行擴充套件,實現該方法的子類有:


前面也說過,當前是 Servlet Web 應用,所以建立的 ApplicationContext 上下文是 AnnotationConfigServletWebServerApplicationContext,執行該類的 postProcessBeanFactory 方法。

public class AnnotationConfigServletWebServerApplicationContext
        extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {

    private final AnnotatedBeanDefinitionReader reader;

    private final ClassPathBeanDefinitionScanner scanner;

    private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();

    private String[] basePackages;
    
    ...
    
    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 先執行父類 ServletWebServerApplicationContext 的 postProcessBeanFactory 方法。
        // 跳轉到 1 檢視父類實現
        super.postProcessBeanFactory(beanFactory);
        
        // basePackages 儲存的是類路徑。先判斷是否為 null,不為 null 則通過 ClassPathBeanDefinitionScanner 的 scan 方法
        // 掃描該路徑下符合條件的 Class,並將 Class 資訊包裝成 BeanDefinition 註冊到容器中,
        // 當然,這裡沒有指定掃描路徑,所以不會進入這個 if。
        // (BeanDefinition 概念會在後面章節詳細討論)
        if (this.basePackages != null && this.basePackages.length > 0) {
            this.scanner.scan(this.basePackages);
        }
        
        // annotatedClasses 儲存的 Class 集合。先判斷該集合是否為空,不為空則通過
        // AnnotatedBeanDefinitionReader 的 register 方法將 Class 資訊包裝成 BeanDefinition 註冊到容器中,
        // 這裡同樣沒有設定 Class 集合內容,所以不會進入這個 if。
        if (!this.annotatedClasses.isEmpty()) {
            this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
        }
    }       
}

1、
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {
    ...

    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 新增 BeanPostProcessor 後置處理器:WebApplicationContextServletContextAwareProcessor,
        // 該後置處理器主要是從 ConfigurableWebApplicationContext 上下文中獲取 ServletContext 和 ServletConfig 物件
        beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
        
        // 新增一個 忽略自動注入的介面
        beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    }
    ...         
}

postProcessBeanFactory 方法執行的操作和前面類似,也是添加了後置處理器和忽略自動注入的介面。

總結

ApplicationContext 上下文準備工作基本結束,主要還是在 BeanFactory 中新增一系列後置處理器、註冊特殊的 Bean 及設定忽略自動注入的介面。其中還提到了 Spring 容器的三個核心部分:Aware 系列介面、BeanPostProcessor 後置處理器、BeanDefinition ,這部分在後面的文章會逐步討論。接下來將對 Spring 容器的核心功能展開討論。




以上就是本章內容,如果文章中有錯誤或者需要補充的請及時提出,本人感激不