1. 程式人生 > 實用技巧 >Spring Boot -- 啟動流程分析二

Spring Boot -- 啟動流程分析二

我們總經常聽說Spring容器,那Spring容器到底是什麼,在介紹建立Spring應用程式上下文之前,我們先來聊一聊Spring容器到底是什麼。

一、容器簡介

容器顧名思義就是用來裝東西的,裝的是什麼?裝的是Bean。

Bean是Spring的基本單位,在基於Spring的web應用中,所有的元件都被當做bBan處理、包括資料來源、Hiberate的SessionFactory、事務管理器等。在Spring中,Bean是一個非常廣義的概念,任何java物件,Java元件都被當做Bean處理。

那容器僅僅是用來儲存Bean這麼簡單麼?不是。

當我們需要某個Bean時,容器會自動幫我們建立,並在適當時銷燬。當某個 Bean 中需建立另一個 Bean 時,也就是 Bean 之間有依賴關係,這種依賴的 Bean 也是由容器自動建立。在外界有一個標準的名詞,前者稱呼為 IOC,也就是控制反轉,後者稱呼為 DI,也就是依賴注入。

1.1、IOC/DI

IOC (Inversion of Control) 控制反轉:所謂控制反轉,就是當我們需要某個 Bean 時,將 Bean 的名稱告知容器,由容器去建立該 Bean,而不是我們手動 new 一個,這裡 Bean 建立管理的控制權都交給了容器,所以這是一種控制權的反轉。其通俗點講就是需要什麼東西讓別人送過來,而不是自己去拿。

DI (Dependency Injection) 依賴注入:就是指當 A Bean 裡面需建立 B Bean 時,會在建立 A Bean 的時候,自動將依賴的 B Bean 注入進去,其 B Bean 是被動接受注入而不是自己主動去找。換句話說就是指 A Bean 不是從容器中查詢它依賴的 B Bean,而是在容器建立 A Bean 候主動將它依賴的 B Bean 注入給它。

IOC 和 DI 其實歸根結底實現的功能是相同的,只是同樣的功能站在不同的角度來闡述罷了。當然,在真實場景中,交由 Spring 容器建立的 Bean 泛指在應用程式中的表現層、業務層、持久層等各層對應的 Bean,如 Controller、Service 等;進行資料互動的模型,如 DTO、VO 等就不需交由 Spring 來建立。

所以,容器本質上可以也可以看作是 Bean 工廠,該工廠管理 Bean 的生命週期,以及 Bean 之間的依賴關係。外界也將 Spring 容器稱為 IOC 容器。當然,這裡容器僅僅是 Spring 的抽象概念,程式碼中將其具象化為 BeanFactory 或 ApplicationContext,容器功能也由具象化的類進行處理。

1.2、容器的結構

容器的實現類並不是唯一的,Spring 框架提供了多個容器的實現,這些容器分為兩套體系:一套是早期的 BeanFactory 體系;還有一套是現在常用的 ApplicationContext,也可稱為應用上下文,它繼承了 BeanFactory,它除了有 BeanFactory 的功能外 ,還提供了其他服務,例如事務和 AOP 服務、國際化(il8n)的訊息源以及應用程式事件處理等企業級的服務。

說到這,就不得不說 Spring 的兩種配置方式,在早期都是 XML 配置檔案的方式,而現在使用的是註解配置的方式。BeanFactory 體系的容器一般用來處理 XML 配置檔案的方式,而 ApplicationContext 體系則都可以處理。

下表列出了BeanFactory 和 ApplicationContext 介面和實現所提供的功能:

功能/特點 BeanFactory ApplicationContext
Bean 例項化/裝配
BeanPostProcessor 自動註冊 沒有
BeanFactoryPostProcessor 自動註冊 沒有
MessageSource 便捷訪問(針對i18n) 沒有
ApplicationEvent 釋出 沒有

兩者還有一個區別是:

  • ApplicationContext 在容器啟動時,一次性建立了所有的 Bean。
  • BeanFactory 在容器啟動時,並未建立 Bean,直到第一次訪問某個 Bean 時才建立目標 Bean。

1.3、BeanFactory

BeanFactory 是容器最基礎的類,它定義了容器的基本功能規範:

public interface BeanFactory {

    // 對 FactoryBean 的轉義定義,因為如果使用 bean 的名字檢索 FactoryBean 得到的物件是工廠生成的物件,
    // 如果需要得到工廠本身,需要轉義(FactoryBean 在後續會詳細介紹)
    String FACTORY_BEAN_PREFIX = "&";
    
    // 根據 bean 的名字,獲取在容器中 bean 例項
    Object getBean(String name) throws BeansException;
    
    //根據 bean 的名字和 Class 型別來得到 bean 例項,增加了型別安全驗證機制。
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    
    // 提供對 bean 的檢索,看看是否在容器有這個名字的 bean
    boolean containsBean(String name);
    
    // 根據 bean 名字,判斷這個 bean 是不是單例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    // 根據 bean 名字,判斷這個 bean 是不是原型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    
    // 根據 bean 名字,判斷是否與指定的型別匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
    // 得到 bean 例項的 Class 型別
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    // 得到bean 的別名,如果根據別名檢索,那麼其原名也會被檢索出來
    String[] getAliases(String name);
}

在 BeanFactory 裡只對容器的基本行為作了定義,其根本不關心你的 Bean 是如何定義怎樣載入的。 正如我們只關心工廠裡得到什麼的產品物件,至於工廠是怎麼生產這些物件的,這個基本的介面不關心。而要知道工廠是如何產生物件的,我們就需要看具體的容器了,也就是 BeanFactory 的子類。

BeanFactory 體系中常用的實現類有:

  • ListableBeanFactory:提供容器中 bean 迭代的功能。如返回所有 Bean 的名字、容器中 Bean 的數量等。
  • HierarchicalBeanFactory:提供父容器的訪問功能,可通過 ConfigurableBeanFactory 的 setParentBeanFactory 方法設定父容器。
  • AutowireCapableBeanFactory:為 Spring 容器之外的 Bean ,也就是未交由 Spring 管理的 Bean ,提供依賴注入的功能。

以上三個是 BeanFactory 的直系親屬,這個三個直系親屬下面又派生了兩個複雜的容器:

  • ConfigurableBeanFactory:其繼承了 HierarchicalBeanFactory 和 SingletonBeanRegistry 這兩個介面,其提供了很多方法,如:定義類載入器、型別轉化、屬性編輯器、註冊依賴 Bean 、銷燬 bean 等,且該介面被大多數的容器繼承、實現。
  • ConfigurableListableBeanFactory:這個介面繼承了 ListableBeanFactory、 AutowireCapableBeanFactory、ConfigurableBeanFactory,自身主要提供用於分析和修改 bean 定義以及預先例項化單例 Bean 的方法。

最後是核心容器:

DefaultListableBeanFactory:它實現了以上所有的介面,在 BeanFactory 體系中可以作為一個獨立的容器使用。 BeanFactory 大致的繼承關係如下:

但我們基本不單獨使用 BeanFactory ,而是直接使用 ApplicationContext ,因為 ApplicationContext 包含了 BeanFactory。

1.4、ApplicationContext

上面說過 ApplicationContext 是 BeanFactory 子類,它不僅包含 BeanFactory 所有功能,還對其進行了擴充套件,而我們喜歡將 ApplicationContext 稱為應用上下文,因為容器只是它的基本功能。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

    // 返回此應用程式上下文的唯一ID
    @Nullable
    String getId();

    // 返回此上下文所屬的應用程式名稱
    String getApplicationName();

    // 返回應用上下文具像化的類名
    String getDisplayName();

    // 返回第一次載入此上下文時的時間戳
    long getStartupDate();

    // 獲取父級應用上下文
    @Nullable
    ApplicationContext getParent();

    // 將 AutowireCapableBeanFactory 介面暴露給外部使用
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

ApplicationContext 自身提供的方法非常簡單,但它繼承了六個介面,來擴充套件自身功能:

  • EnvironmentCapable:獲取 Environment。
  • ListableBeanFactory、HierarchicalBeanFactory:這是 BeanFactory 體系介面,分別提供 Bean 迭代和訪問父容器的功能。
  • MessageSource:支援國際化功能。
  • ApplicationEventPublisher:應用事件釋出器,封裝事件釋出功能的介面。
  • ResourcePatternResolver:該介面繼承至 ResourceLoader ,作用是載入多個 Resource。

ApplicationContext 同樣提供了非常多的實現類,其又可細分為兩大類, ConfigurableApplicationContext 和 WebApplicationContext。

1.5、ConfigurableApplicationContext

該介面是比較重要的一個介面,幾乎所有的應用上下文都實現了該介面。該介面在ApplicationContext的基礎上提供了配置應用上下文的能力,此外提供了生命週期的控制能力。

public

interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { // 應用上下文配置時,這些符號用於分割多個配置路徑 String CONFIG_LOCATION_DELIMITERS = ",; \t\n"; // BeanFactory中,ConversionService類所對應的bean的名字。如果沒有此類的例項的話嗎,則使用預設的轉換規則 String CONVERSION_SERVICE_BEAN_NAME = "conversionService"; //LoadTimeWaver類所對應的Bean在容器中的名字。如果提供了該例項,上下文會使用臨時的 ClassLoader ,這樣,LoadTimeWaver就可以使用bean確切的型別了 String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver"; // Environment 類在容器中例項的名字 String ENVIRONMENT_BEAN_NAME = "environment"; // System 系統變數在容器中對應的Bean的名字 String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties"; // System 環境變數在容器中對應的Bean的名字 String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment"; // 設定容器的唯一ID void setId(String id); // 設定此容器的父容器 void setParent(@Nullable ApplicationContext parent); // 設定容器的 Environment 變數 void setEnvironment(ConfigurableEnvironment environment); // 以 ConfigurableEnvironment 的形式返回此容器的環境變數。以使使用者更好的進行配置 @Override ConfigurableEnvironment getEnvironment(); // 此方法一般在讀取應用上下文配置的時候呼叫,用以向此容器中增加BeanFactoryPostProcessor。增加的Processor會在容器refresh的時候使用。 void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor); // 向容器增加一個 ApplicationListener,增加的 Listener 用於釋出上下文事件,如 refresh 和 shutdown 等 void addApplicationListener(ApplicationListener<?> listener); // 向容器中注入給定的 Protocol resolver void addProtocolResolver(ProtocolResolver resolver); // 這是初始化方法,因此如果呼叫此方法失敗的情況下,要將其已經建立的 Bean 銷燬。 // 換句話說,呼叫此方法以後,要麼所有的Bean都例項化好了,要麼就一個都沒有例項化 void refresh() throws BeansException, IllegalStateException; // 向JVM註冊一個回撥函式,用以在JVM關閉時,銷燬此應用上下文 void registerShutdownHook(); // 關閉此應用上下文,釋放其所佔有的所有資源和鎖。並銷燬其所有建立好的 singleton Beans @Override void close(); // 檢測此 FactoryBean 是否被啟動過 boolean isActive(); // 返回此應用上下文的容器。 // 千萬不要使用此方法來對 BeanFactory 生成的 Bean 做後置處理,因為單例 Bean 在此之前已經生成。 // 這種情況下應該使用 BeanFactoryPostProcessor 來在 Bean 生成之前對其進行處理 ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException; }

該介面下又有幾個重要的實現類:

  • AbstractApplicationContext:這是個抽象類,僅實現了公共的上下文特性。這個抽象類使用了模板方法設計模式,需要具體的實現類去實現這些抽象的方法。
  • GenericApplicationContext:該類繼承自 AbstractApplicationContext,是為通用目的設計的,它能載入各種配置檔案,例如 xml,properties 等等。它的內部持有一個 DefaultListableBeanFactory 的例項,實現了 BeanDefinitionRegistry 介面,以便允許向其應用任何 bean 的定義的讀取器。
  • AnnotationConfigApplicationContext:該類繼承自 GenericApplicationContext ,提供了註解配置(例如:@Configuration、@Component等)和類路徑掃描(scan方法)的支援。

1.6、WebApplicationContext

該介面是專門為 Web 應用準備的,其允許從相對於 Web 根目錄的路徑中裝載配置檔案完成初始化。

public interface WebApplicationContext extends ApplicationContext {

    // 整個 Web 應用上下文是作為屬性放置在 ServletContext 中的,該常量就是應用上下文在 ServletContext 屬性列表中的 key
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

    // 定義了三個作用域的名稱
    String SCOPE_REQUEST = "request";
    String SCOPE_SESSION = "session";
    String SCOPE_APPLICATION = "application";

    // 在工廠中的 bean 名稱
    String SERVLET_CONTEXT_BEAN_NAME = "servletContext";

    // ServletContext 初始化引數名稱
    String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";

    // 在工廠中 ServletContext 屬性值環境bean的名稱
    String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";

    // 用來獲取 ServletContext 物件
    @Nullable
    ServletContext getServletContext();
}

該介面的核心實現類有:

  • ConfigurableWebApplicationContext:該介面同時繼承了 WebApplicationContext 和 ConfigurableApplicationContext,提供了 Web 應用上下文的可配置的能力。
  • GenericWebApplicationContext:該類繼承自 GenericApplicationContext,實現了 ConfigurableWebApplicationContext。
  • XmlWebApplicationContext:該上下文是使用 Xml 配置檔案的方式,不過是在 Web 環境中使用的。
  • AnnotationConfigServletWebServerApplicationContext:該類是被 SpringBoot 擴充套件而來的,SpringBoot 使用的就是該上下文。

二、ApplicationContext準備啟動

我們接著上一節繼續分析run方法中的核心部分程式碼:應用上下文的建立。

2.1、ApplicationContext的建立

public class SpringApplication {

    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";

    public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
            
    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
            
    ...
    
    private WebApplicationType webApplicationType;
    
    ...
    
    public ConfigurableApplicationContext run(String... args) {
        ...
        
        ConfigurableApplicationContext context = null;
        
        try {
            ...
            // 1、通過 createApplicationContext 方法建立對應的 ApplicationContext 應用上下文,進入 1.1 檢視具體實現
            context = createApplicationContext();
            
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            
            // 2、該方法實質是啟動 Spring 應用上下文的,但 Spring Boot 嵌入式容器也在該過程中被啟動,入參是上下文物件,我們進入 2.1 進行跟蹤
            refreshContext(context);
            
            ...
        }
        ...
    }
    
    // 1.1、
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
            
                // 這裡就是通過 webApplicationType 屬性,判斷應用型別,來建立不同的 ApplicationContext 應用上下文
                switch (this.webApplicationType) {
                case SERVLET:
                    
                    // 返回的是 Servlet Web ,具體物件為 AnnotationConfigServletWebServerApplicationContext,
                    // 該類有一個關鍵父類 ServletWebServerApplicationContext
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                
                    // 返回的是 Reactive Web,具體物件為 AnnotationConfigReactiveWebServerApplicationContext
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                
                    // 應用型別是非 Web 時,返回 AnnotationConfigApplicationContext
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            ...
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    
    // 2.1
    private void refreshContext(ConfigurableApplicationContext context) {
        
        // 裡面呼叫的是 refresh 方法,進入 2.2 繼續跟蹤
        refresh(context);
        
        ...
    }
    
    // 2.2
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        
        // 最終呼叫了 所有應用上下文的統一抽象類 AbstractApplicationContext 中的 refresh 方法,進入 3 檢視實現
        ((AbstractApplicationContext) applicationContext).refresh();
    }
    
    ...
}

這裡也是通過 webApplicationType 屬性來確定應用型別從而建立 String 上下文,上篇文章說到該屬性值是在 Spring Boot 準備階段推匯出來的。這裡我們的應用型別是 Servlet ,所以建立的是 AnnotationConfigServletWebServerApplicationContext 物件。

2.2、AnnotationConfigServletWebServerApplicationContext 建構函式

通過BeanUtils.instantiateClass會呼叫AnnotationConfigServletWebServerApplicationContext 的無參建構函式,而在Java的繼承中,會先呼叫父類的構造方法。所以會先呼叫AnnotationConfigServletWebServerApplicationContext的父類GeniricApplicationContext的構造方法:

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

    private final DefaultListableBeanFactory beanFactory;

    @Nullable
    private ResourceLoader resourceLoader;

    private boolean customClassLoader = false;

    private final AtomicBoolean refreshed = new AtomicBoolean();


    /**
     * Create a new GenericApplicationContext.
     * @see #registerBeanDefinition
     * @see #refresh
     */
    public GenericApplicationContext() {
        this.beanFactory = new DefaultListableBeanFactory();
    }
     ...
}

在父類中初始化beanFactory,即直接new了一個DefaultListableBeanFactory:

    /**
     * Create a new {@link AnnotationConfigServletWebServerApplicationContext} that needs
     * to be populated through {@link #register} calls and then manually
     * {@linkplain #refresh refreshed}.
     */
    public AnnotationConfigServletWebServerApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

在建構函式中通過new AnnotatedBeanDefinitionReader(this)例項化了一個Bean讀取器:

    /**
     * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry.
     * <p>If the registry is {@link EnvironmentCapable}, e.g. is an {@code ApplicationContext},
     * the {@link Environment} will be inherited, otherwise a new
     * {@link StandardEnvironment} will be created and used.
     * @param registry the {@code BeanFactory} to load bean definitions into,
     * in the form of a {@code BeanDefinitionRegistry}
     * @see #AnnotatedBeanDefinitionReader(BeanDefinitionRegistry, Environment)
     * @see #setEnvironment(Environment)
     */
    public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
        this(registry, getOrCreateEnvironment(registry));
    }

    /**
     * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry,
     * using the given {@link Environment}.
     * @param registry the {@code BeanFactory} to load bean definitions into,
     * in the form of a {@code BeanDefinitionRegistry}
     * @param environment the {@code Environment} to use when evaluating bean definition
     * profiles.
     * @since 3.1
     */
    public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        Assert.notNull(environment, "Environment must not be null");
        this.registry = registry;
        this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

最後會呼叫到AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry,Object source)方法:

    /**
     * Register all relevant annotation post processors in the given registry.
     * @param registry the registry to operate on
     * @param source the configuration source element (already extracted)
     * that this registration was triggered from. May be {@code null}.
     * @return a Set of BeanDefinitionHolders, containing all bean definitions
     * that have actually been registered by this call
     */
    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
            BeanDefinitionRegistry registry, @Nullable Object source) {

        DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
        if (beanFactory != null) {
            if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
                beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
            }
            if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
                beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
            }
        }

        Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

        if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
        if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
        if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition();
            try {
                def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                        AnnotationConfigUtils.class.getClassLoader()));
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
            }
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
        }

        if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
        }

        return beanDefs;
    }

這個方法向BeanDefinitionMap中添加了5個類:

/** Map of bean definition objects, keyed by bean name. */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

這幾個類分別為ConfigurationClassPostProcessor、DefaultEventListenerFactory、EventListenerMethodProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor。

接著 呼叫this.scanner = new ClassPathBeanDefinitionScanner(this)來初始化一個掃描器,這個掃描器在後面掃描包的時候,並沒有用到,

2.3、prepareContext

prepareContext(context, environment, listeners, applicationArguments, printedBanner);

建立完 Spring 應用上下文之後,執行 prepareContext 方法進入準備上下文階段:

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

我們來看看主要做了哪些操作:

1、設定了 Spring 應用上下文的 ApplicationArguments,上面說過是處理外部化配置的,具體型別為 StandardServletEnvironment 。

2、Spring 應用上下文後置處理,主要是覆蓋當前 Spring 應用上下文預設所關聯的 ResourceLoader 和 ClassLoader,以及初始化beanFactory的conversionService。

3、執行 Spring 的初始化器,上篇文章說過在 Spring Boot 準備階段初始化了一批在 spring.factories 檔案中定義好的 ApplicationContextInitializer ,這裡就是執行它們的 initialize 方法,同時嚮應用上下文beanFactoryPostProcessors列表新增兩個物件:

protected void applyInitializers(ConfigurableApplicationContext context) {
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                    ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
/** BeanFactoryPostProcessors to apply on refresh. */
    private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();

4、執行 SpringApplicationRunListeners 的 contextPrepared 階段方法,表示 ApplicationContext 準備完成,同時向 Spring Boot 監聽器釋出 ApplicationContextInitializedEvent 事件 。

5、將 springApplicationArguments 和 springBootBanner 註冊為 Bean。

/** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

6、載入 Spring 應用上下文的配置源到BeanDefinitionMap,也是在上篇文章 Spring Boot 準備階段獲取的 primarySources ,primarySources 來源於 SpringApplication 構造器引數;

7、最後執行 SpringApplicationRunListeners 的 contextLoaded 階段方法,表示 ApplicationContext 完成載入但還未啟動,同時向 Spring Boot 監聽器釋出 ApplicationPreparedEvent 事件 。

2.4、refreshContext

接下來就是真正啟動階段,執行的是 refreshContext 方法:

    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }

這裡,最終也是呼叫 ApplicationContext 的 refresh 方法來啟動上下文:

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

可以看到,底層呼叫的是 AbstractApplicationContext 的 refresh 方法,

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 初始化 refresh 的上下文環境,就是記錄下容器的啟動時間、標記已啟動狀態、處理配置檔案中的佔位符
        prepareRefresh();

        // 2. 初始化 BeanFactory,載入並解析配置
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

        /* ---至此,已經完成了簡單容器的所有功能,下面開始對簡單容器進行增強--- */

        // 3. 對 BeanFactory 進行功能增強,如設定BeanFactory的類載入器,新增幾個 BeanPostProcessor,手動註冊幾個特殊的 bean
        prepareBeanFactory(beanFactory);

        try {
            // 4. 後置處理 beanFactory,新增後置處理器
            postProcessBeanFactory(beanFactory);

            // 5. 呼叫已註冊的 BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);

            // 6. 註冊 BeanPostProcessor,僅僅是註冊,呼叫在getBean的時候
            registerBeanPostProcessors(beanFactory);

            // 7. 初始化國際化資源
            initMessageSource();

            // 8. 初始化事件廣播器
            initApplicationEventMulticaster();

            // 9. 留給子類實現的模板方法
            onRefresh();

            // 10. 註冊事件監聽器
            registerListeners();

            // 11. 例項化所有非延遲載入的單例
            finishBeanFactoryInitialization(beanFactory);

            // 12. 完成重新整理過程,釋出應用事件
            finishRefresh();
            
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
            }
            
            // 13.銷燬已經初始化的 singleton 的 Beans,以免有些 bean 會一直佔用資源
            this.destroyBeans();
            
            // Reset 'active' flag.
            this.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...
            this.resetCommonCaches();
        }
    }
}

三、refresh分析

3.1、prepareRefresh

我們先從 refresh 中的 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 方法執行結束,主要是記錄容器的啟動時間、活動狀態、檢查必備屬性是否存在。

3.2、obtainFreshBeanFactory

接著進入 refresh 中的 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;
    }

之後,該方法還返回了 BeanFactory 物件,從這也可以看出 ApplicationContext 底層是以 BeanFactory 為基礎,逐步擴充套件 Spring 容器功能。

3.3、prepareBeanFactory

接著進入 refresh 中的 prepareBeanFactory 方法。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 。

    /**
     * Dependency interfaces to ignore on dependency check and autowire, as Set of
     * Class objects. By default, only the BeanFactory interface is ignored.
     */
    private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();

    /** BeanPostProcessors to apply in createBean. */
    private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

/** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

3.4、postProcessBeanFactory

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

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 方法執行的操作和前面類似,也是添加了後置處理器和忽略自動注入的介面。

四、BeanDefinition

原計劃是對接下來的 invokeBeanFactoryPostProcessors 方法進行討論,但該方法涉及 Spring 中一個非常重要的概念: BeanDefinition,所以,這裡先對 BeanDefinition 進行討論,這樣也有利於完善 Spring 的知識體系。

現如今,我們一般獲取物件的方式有兩種:

  • 一種是手動直接 new;
  • 另一種是交給 Spring 管理,Spring 將管理的物件稱之為 Bean,容器會先例項化 Bean,然後自動注入,例項化的過程就需要依賴 BeanDefinition。

BeanDefinition 用於儲存 Bean 的相關資訊,包括屬性、構造方法引數、依賴的 Bean 名稱及是否單例、延遲載入等,它是例項化 Bean 的原材料,Spring 就是根據 BeanDefinition 中的資訊例項化 Bean。

BeanDefinition 是一個介面,它有多個實現類,這些實現類分別描述不同型別的 Bean。

4.1、BeanDefinition

一個 BeanDefinition 描述了一個 Bean 例項,例項包含屬性值、構造方法引數值以及更多實現資訊。該 BeanDefinition 只是是一個最小的介面,主要目的是允許修改屬性值和其他 Bean 元資料,這裡列出幾個核心方法。

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    // 單例、原型識別符號
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

    // 標識 Bean 的類別,分別對應 使用者定義的 Bean、來源於配置檔案的 Bean、Spring 內部的 Bean
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;

    // 設定、返回 Bean 的父類名稱
    void setParentName(@Nullable String parentName);
    String getParentName();

    // 設定、返回 Bean 的 className
    void setBeanClassName(@Nullable String beanClassName);
    String getBeanClassName();

    // 設定、返回 Bean 的作用域
    void setScope(@Nullable String scope);
    String getScope();

    // 設定、返回 Bean 是否懶載入
    void setLazyInit(boolean lazyInit);
    boolean isLazyInit();
    
    // 設定、返回當前 Bean 所依賴的其它 Bean 名稱。
    void setDependsOn(@Nullable String... dependsOn);
    String[] getDependsOn();
    
    // 設定、返回 Bean 是否可以自動注入。只對 @Autowired 註解有效
    void setAutowireCandidate(boolean autowireCandidate);
    boolean isAutowireCandidate();
    
    // 設定、返回當前 Bean 是否為主要候選 Bean 。
    // 當同一個介面有多個實現類時,通過該屬性來配置某個 Bean 為主候選 Bean。
    void setPrimary(boolean primary);
    boolean isPrimary();

    // 設定、返回建立該 Bean 的工廠類。
    void setFactoryBeanName(@Nullable String factoryBeanName);
    String getFactoryBeanName();
    
    // 設定、返回建立該 Bean 的工廠方法
    void setFactoryMethodName(@Nullable String factoryMethodName);
    String getFactoryMethodName();
    
    // 返回該 Bean 構造方法引數值、所有屬性
    ConstructorArgumentValues getConstructorArgumentValues();
    MutablePropertyValues getPropertyValues();

    // 返回該 Bean 是否是單例、是否是非單例、是否是抽象的
    boolean isSingleton();
    boolean isPrototype();
    boolean isAbstract();

    // 返回 Bean 的類別。類別對應上面的三個屬性值。
    int getRole();

    ...
}

可以看到 BeanDefinition 介面提供了一系列操作 Bean 元資料的set、get方法,這些操作為 Bean 的描述定義了一套模板,具體的實現則交由子類。

4.2、AnnotatedBeanDefinition

AnnotatedBeanDefinition 是 BeanDefinition 子介面之一,該介面擴充套件了 BeanDefinition 的功能,其用來操作註解元資料。一般情況下,通過註解方式得到的 Bean(@Component、@Bean),其 BeanDefinition 型別都是該介面的實現類。

public interface AnnotatedBeanDefinition extends BeanDefinition {

    // 獲得當前 Bean 的註解元資料
    AnnotationMetadata getMetadata();

    // 獲得當前 Bean 的工廠方法上的元資料
    MethodMetadata getFactoryMethodMetadata();
}

該介面可以返回兩個元資料的類:

  • AnnotationMetadata:主要對 Bean 的註解資訊進行操作,如:獲取當前 Bean 標註的所有註解、判斷是否包含指定註解。
  • MethodMetadata:方法的元資料類。提供獲取方法名稱、此方法所屬類的全類名、是否是抽象方法、判斷是否是靜態方法、判斷是否是final方法等 ;

4.3、AbstractBeanDefinition

AbstractBeanDefinition 是 BeanDefinition 的子抽象類,也是其他 BeanDefinition 型別的基類,其實現了介面中定義的一系列操作方法,並定義了一系列的常量屬性,這些常量會直接影響到 Spring 例項化 Bean 時的策略。核心屬性如下。

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
        implements BeanDefinition, Cloneable {

    // 預設的 SCOPE,預設是單例
    public static final String SCOPE_DEFAULT = "";

    // 不進行自動裝配
    public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
    // 根據 Bean 的名字進行自動裝配,byName
    public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
    // 根據 Bean 的型別進行自動裝配,byType
    public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
    // 根據構造器進行自動裝配
    public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
    // 首先嚐試按構造器自動裝配。如果失敗,再嘗試使用 byType 進行自動裝配。(Spring 3.0 之後已廢除)
    public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;

    // 通過依賴檢查來檢視 Bean 的每個屬性是否都設定完成
    // 以下常量分別對應:不檢查、對依賴物件檢查、對基本型別,字串和集合進行檢查、對全部屬性進行檢查
    public static final int DEPENDENCY_CHECK_NONE = 0;
    public static final int DEPENDENCY_CHECK_OBJECTS = 1;
    public static final int DEPENDENCY_CHECK_SIMPLE = 2;
    public static final int DEPENDENCY_CHECK_ALL = 3;

    // 關閉應用上下文時需呼叫的方法名稱
    public static final String INFER_METHOD = "(inferred)";

    // 存放 Bean 的 Class 物件
    private volatile Object beanClass;

    // Bean 的作用範圍
    private String scope = SCOPE_DEFAULT;

    // 非抽象
    private boolean abstractFlag = false;
    // 非延遲載入
    private boolean lazyInit = false;
    // 預設不自動裝配
    private int autowireMode = AUTOWIRE_NO;
    // 預設不依賴檢查
    private int dependencyCheck = DEPENDENCY_CHECK_NONE;

    // 依賴的 Bean 列表
    private String[] dependsOn;

    // 可以作為自動裝配的候選者,意味著可以自動裝配到其他 Bean 的某個屬性中
    private boolean autowireCandidate = true;
    
    // 建立當前 Bean 例項工廠類名稱
    private String factoryBeanName;
    // 建立當前 Bean 例項工廠類中方法名稱
    private String factoryMethodName;

    // 儲存構造方法的引數
    private ConstructorArgumentValues constructorArgumentValues;
    // 儲存 Bean 屬性名稱以及對應的值
    private MutablePropertyValues propertyValues;
    // 儲存被覆蓋的方法資訊
    private MethodOverrides methodOverrides;

    // init、destroy 方法名稱
    private String initMethodName;
    private String destroyMethodName;

    // 是否執行 init 和 destroy 方法
    private boolean enforceInitMethod = true;
    private boolean enforceDestroyMethod = true;

    // Bean 是否是使用者定義的而不是應用程式本身定義的
    private boolean synthetic = false;

    // Bean 的身份類別,預設是使用者定義的 Bean
    private int role = BeanDefinition.ROLE_APPLICATION;

    // Bean 的描述資訊
    private String description;

    // Bean 定義的資源
    private Resource resource;
    
    ...
}

以上是 AbstractBeanDefinition 中定義的一些常量和屬性,該類中還有一部分是操作這些屬性的 set 和 get 方法,這些方法都由子類來操作,且應用程式中真正使用的也是這些子類 BeanDefinition。 先來看 AbstractBeanDefinition 直接實現類:RootBeanDefinition、GenericBeanDefinition、ChildBeanDefinition。

4.4、RootBeanDefinition

該類繼承自 AbstractBeanDefinition,它可以單獨作為一個 BeanDefinition,也可以作為其他 BeanDefinition 的父類。 RootBeanDefinition 在 AbstractBeanDefinition 的基礎上定義了更多屬性。

public class RootBeanDefinition extends AbstractBeanDefinition {

    // BeanDefinitionHolder 儲存 Bean 的名稱、別名、BeanDefinition
    private BeanDefinitionHolder decoratedDefinition;

    // AnnotatedElement 是java反射包的介面,通過它可以檢視 Bean 的註解資訊
    private AnnotatedElement qualifiedElement;

    // 允許快取
    boolean allowCaching = true;
    
    // 工廠方法是否唯一
    boolean isFactoryMethodUnique = false;

    // 封裝了 java.lang.reflect.Type,提供了泛型相關的操作
    volatile ResolvableType targetType;

    // 快取 Class,表示 RootBeanDefinition 儲存哪個類的資訊
    volatile Class<?> resolvedTargetType;

    // 快取工廠方法的返回型別
    volatile ResolvableType factoryMethodReturnType;

    // 這是以下四個構造方法欄位的通用鎖
    final Object constructorArgumentLock = new Object();
    // 用於快取已解析的構造方法或工廠方法
    Executable resolvedConstructorOrFactoryMethod;
    // 將構造方法引數標記為已解析
    boolean constructorArgumentsResolved = false;
    // 用於快取完全解析的構造方法引數
    Object[] resolvedConstructorArguments;
    // 快取待解析的構造方法引數
    Object[] preparedConstructorArguments;

    // 這是以下兩個後處理欄位的通用鎖
    final Object postProcessingLock = new Object();
    // 表明是否被 MergedBeanDefinitionPostProcessor 處理過
    boolean postProcessed = false;
    // 在生成代理的時候會使用,表明是否已經生成代理
    volatile Boolean beforeInstantiationResolved;

    // 實際快取的型別是 Constructor、Field、Method 型別
    private Set<Member> externallyManagedConfigMembers;

    // InitializingBean中 的 init 回撥函式名 afterPropertiesSet 會在這裡記錄,以便進行生命週期回撥
    private Set<String> externallyManagedInitMethods;

    // DisposableBean 的 destroy 回撥函式名 destroy 會在這裡記錄,以便進生命週期回撥
    private Set<String> externallyManagedDestroyMethods;

    ...
}

4.5、ChildBeanDefinition

該類繼承自 AbstractBeanDefinition。其相當於一個子類,不可以單獨存在,必須依賴一個父 BeanDetintion,構造 ChildBeanDefinition 時,通過構造方法傳入父 BeanDetintion 的名稱或通過 setParentName 設定父名稱。它可以從父類繼承方法引數、屬性值,並可以重寫父類的方法,同時也可以增加新的屬性或者方法。若重新定義 init 方法,destroy 方法或者靜態工廠方法,ChildBeanDefinition 會重寫父類的設定。

從 Spring 2.5 開始,以程式設計方式註冊 Bean 定義的首選方法是 GenericBeanDefinition,GenericBeanDefinition 可以有效替代 ChildBeanDefinition 的絕大分部使用場合。

4.6、GenericBeanDefinition

GenericBeanDefinition 是 Spring 2.5 以後新引入的 BeanDefinition,是 ChildBeanDefinition 更好的替代者,它同樣可以通過 setParentName 方法設定父 BeanDefinition。

4.7、 ConfigurationClassBeanDefinition

該類繼承自 RootBeanDefinition ,並實現了 AnnotatedBeanDefinition 介面。這個 BeanDefinition 用來描述在標註 @Configuration 註解的類中,通過 @Bean 註解例項化的 Bean。

其功能特點如下:

1、如果 @Bean 註解沒有指定 Bean 的名字,預設會用方法的名字命名 Bean。

2、標註 @Configuration 註解的類會成為一個工廠類,而標註 @Bean 註解的方法會成為工廠方法,通過工廠方法例項化 Bean,而不是直接通過構造方法初始化。

3、標註 @Bean 註解的類會使用構造方法自動裝配

4.8、AnnotatedGenericBeanDefinition

該類繼承自 GenericBeanDefinition ,並實現了 AnnotatedBeanDefinition 介面。這個 BeanDefinition 用來描述標註 @Configuration 註解的 Bean。

4.9、ScannedGenericBeanDefinition

該類繼承自 GenericBeanDefinition ,並實現了 AnnotatedBeanDefinition 介面。 這個 BeanDefinition 用來描述標註 @Component 註解的 Bean,其派生註解如 @Service、@Controller 也同理。

最後,我們來做個總結。BeanDefinition 主要是用來描述 Bean,其儲存了 Bean 的相關資訊,Spring 例項化 Bean 時需讀取該 Bean 對應的 BeanDefinition。BeanDefinition 整體可以分為兩類,一類是描述通用的 Bean,還有一類是描述註解形式的 Bean。一般前者在 XML 時期定義 <bean> 標籤以及在 Spring 內部使用較多,而現今我們大都使用後者,通過註解形式載入 Bean。

參考文章:

[1]Spring(二)核心容器 - 簡介 、BeanFactory、ApplicationContext(轉載)

[2]Spring(三)核心容器 - ApplicationContext 上下文啟動準備(轉載)

[3]Spring(四)核心容器 - BeanDefinition 解析(轉載)