Spring原始碼系列:核心概念解析

2023-04-20 18:00:47

前言

本文旨在為讀者解析Spring原始碼中的關鍵類,以便讀者在深入閱讀原始碼時,能夠了解關鍵類的作用和用途。在閱讀Spring原始碼時,經常會遇到一些不熟悉的概念,瞭解關鍵類的作用可以幫助讀者更好地理解這些概念。

BeanDefinition

BeanDefinition是Spring框架中的一個重要概念,它定義了一個Bean的基本屬性和行為,比如:

  1. BeanClassName,當前的bean名字
  2. Scope,是否單例,具體列舉:#SCOPE_SINGLETON、#SCOPE_PROTOTYPE
  3. LazyInit,是否懶載入,預設不是
  4. DependsOn,是否依賴其他bean,如果依賴,則會先建立依賴bean
  5. InitMethodName,初始化方法名稱
  6. DestroyMethodName,銷燬類方法名稱
  7. ......還有更多,但是這幾個大體已經差不多了

BeanDefinition的作用非常重要,它可以幫助Spring容器更好地管理Bean的生命週期和依賴關係。在Spring框架中,我們經常會通過註解方式來定義Bean:

  1. < bean/>
  2. @Bean
  3. @Component(@Controller、@Service)

這些都是被稱為申明式定義Bean。就是使用Spring提供好的封裝。

除了註解方式,我們還可以通過程式設計方式來定義Bean,這時就需要直接使用BeanDefinition來建立BeanDefinition物件,並設定對應的屬性,然後將其註冊到Spring容器中,比如

    // 建立一個Spring容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition.setBeanClass(UserService.class);
        //當然還可以設定其他上面我說的其他屬性:懶載入什麼的
        applicationContext.registerBeanDefinition("userService", beanDefinition);

        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.test();

無論是通過註解方式還是程式設計方式來定義Bean,最終都是需要使用BeanDefinition來描述Bean的基本屬性和行為,然後將其放入Spring容器中進行管理。

BeanDefinitionReader

BeanDefinitionReader是Spring框架中的一個重要元件,主要用於讀取和操作BeanDefinition物件。雖然我們在使用Spring框架時很少直接使用BeanDefinitionReader,但在Spring原始碼中卻扮演著非常重要的角色,相當於Spring原始碼的基礎設施。

BeanDefinitionReader的核心方法包括以下幾個:

  1. BeanDefinitionRegistry,用來註冊bean定義,相當於一個工廠
  2. BeanNameGenerator,用來生成bean名字的生成器
  3. loadBeanDefinitions,從資源中載入bean

XmlBeanDefinitionReader

XmlBeanDefinitionReader是BeanDefinitionReader的子類,可以用於從XML檔案中讀取BeanDefinition並註冊到Spring容器中。使用XmlBeanDefinitionReader的步驟如下:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);
//載入xml中設定的所有<bean>
int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");
System.out.println(context.getBean("userService"))

AnnotatedBeanDefinitionReader

細心的朋友,應該可以發現AnnotatedBeanDefinitionReader是一個單獨的類,不是BeanDefinitionReader的子類,但它的方法與BeanDefinitionReader基本相同,官方說是方便的介面卡,用於程式設計註冊bean類,他可以解析@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description相關注解,具體操作如下:

// 建立一個Spring容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

//        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
//        beanDefinition.setBeanClass(UserService.class);
//        applicationContext.registerBeanDefinition("userService", beanDefinition);

        new AnnotatedBeanDefinitionReader(applicationContext).registerBean(UserService.class);

        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.test();

同樣的,他也可以讓我們註冊的bean走完建立的整個生命週期過程。

ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner也是一個用於註冊BeanDefinition的工具類,與BeanDefinition介面沒有直接關係。ClassPathBeanDefinitionScanner可以掃描指定包路徑下帶有特定註解的類,並將其解析成BeanDefinition,註冊到Spring容器中。主要是他有個scan方法對我們定義的basepackage包路徑進行解析掃描所有帶有@component、@ManagedBean(JSR-250標準)、@Named(JSR-330標準)
使用ClassPathBeanDefinitionScanner的步驟如下:

// 建立一個Spring容器  
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);  
  
new ClassPathBeanDefinitionScanner(applicationContext).scan("com.xiaoyu");  
UserService userService = (UserService) applicationContext.getBean("userService");  
userService.test();

BeanFactory

BeanFactory是Spring框架中的一個重要介面,他就是Spring用於管理Bean物件的建立和管理,看他的幾個主要方法就知道了:

  1. getBean,可以根據name、type等獲取bean物件
  2. containsBean,是否bean工廠中有某個物件
  3. isSingleton,判斷是否是單例
  4. isTypeMatch,判斷改name是否匹配型別
  5. getType,根據bean名字獲取型別
  6. getAliases。獲取別名陣列
    看著主要幾個介面實現,基本都是圍繞bean所做的,然後根據介面再看他的實現類就方便許多了,

DefaultListableBeanFactory

如果看過原始碼的朋友肯定對這個實現類不陌生,如果對這個實現類陌生的朋友,那請記住這個重要的實現類,它實現了很多介面、且繼承了多層父類別,所以他的功能也是相當之多。我們來看看他的主要方法:

  1. containsBeanDefinition,檢視是否包含某個bean定義,因為該類維護了一個Map<String, BeanDefinition> beanDefinitionMap屬性。
  2. determineAutowireCandidate,決定注入哪個bean,@Primary-->優先順序最高--->name
  3. doResolveDependency,解析依賴,進行注入
  4. registerBeanDefinition,註冊bean定義到beanDefinitionMap屬性
  5. preInstantiateSingletons,進行建立bean範例

具體使用操作也是基本類似的

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();  
beanDefinition.setBeanClass(UserService.class);  
defaultListableBeanFactory.registerBeanDefinition("userService",beanDefinition);  
  
UserService userService1 = (UserService) defaultListableBeanFactory.getBean("userService");  
userService1.test();

從他的結構圖也能看出來:

AbstractBeanFactory

該類是抽象bean,介紹他主要目的就是getbean時,走的主要邏輯就是該類實現的dogetbean方法(請記住這個重要的方法),所以確實需要關注下,主要方法如下:

  1. doGetBean,獲取bean的主要邏輯,沒有則建立
  2. getMergedBeanDefinition,bean定義的合併邏輯,主要是將父類別beanfactory中的屬性被子類覆蓋

AbstractAutowireCapableBeanFactory

繼承自剛才提到的AbstractBeanFactory,主要方法如下:

  1. autowireByName,按照name注入
  2. autowireByType,根據型別
  3. createBean,建立bean流程,範例化前可以使用BeanPostProcessors後置處理器
  4. createBeanInstance,正在建立bean,這邊使用到了之前入門講過的推斷構造器實現範例化
  5. doCreateBean,建立bean,迴圈依賴、屬性填充、初始化
  6. initializeBean,初始化bean,包括初始化前、初始化、初始化後
  7. instantiateUsingFactoryMethod,利用factorymethod初始化bean
  8. invokeAwareMethods,初始化bean時的回撥函數Aware介面
  9. populateBean,初始化之前,屬性賦值

可以從他的關鍵方法看出,主要作用就是初始化bean的全過程,也是很重要的類

HierarchicalBeanFactory

這裡說下HierarchicalBeanFactory類,他只是一個介面類,但是如果想要使用beanfactory的層次結構,例如獲取父beanfactory,那就必須實現HierarchicalBeanFactory類,比如前面說的bean定義的合併邏輯,就需要獲取父beanfactory,從而實現父子bean定義的覆蓋合併

ApplicationContext

ApplicationContext是個介面,實際上也是一個BeanFactory,不過比BeanFactory
更加強大,它本身並沒有太多方法,但是它繼承了很多介面,因為介面之間是可以多繼承的。

關於他的父介面,這裡不做多說明,詳情的話請看下子文章(後續更新)。

AnnotationConfigApplicationContext

一看這個類,大家都知道了,我們用的範例全是用這個類去啟動我們的spring的,我們看看他的主要方法:

  1. AnnotationConfigApplicationContext,構造器,會初始化DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner;然後開始呼叫refresh()方法。
  2. register,會使用程式設計式定義將bean注入spring容器。就如我們的APPConfig
  3. scan,走ClassPathBeanDefinitionScanner的scan,掃描包路徑,將宣告式的bean注入進spring容器
  4. setBeanNameGenerator,bean名稱生成器

ClassPathXmlApplicationContext

主要就是去解析xml設定的bean定義將其注入到spring容器中,功能其實跟AnnotationConfigApplicationContext類似,但是卻沒有AnnotationConfigApplicationContext強大,比如不能註冊BeanDefinition。

BeanPostProcessor

BeanPostProcess表示Bena的後置處理器,可以有多個BeanPostProcessor,我們自己也可以去定義一個BeanPostProcessor;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if ("userService".equals(beanName)) {
            System.out.println("userService");
            return new User();
        }
        System.out.println("MyBeanPostProcessor.postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("MyBeanPostProcessor.postProcessAfterInitialization");
        return bean;
    }

我們可以通過實現bean的後置處理器,來對某一個bean或者所有bean的進行干預,博主只是隨便寫了一個,沒有什麼太大意義。

BeanFactoryPostProcessor

BeanFactoryPostProcessor表示Bean工廠的後置處理器,其實和BeanPostProcessor類似,BeanPostProcessor是干涉Bean的建立過程,BeanFactoryPostProcessor是干涉BeanFactory的建立過程,我們也可以自定義:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("加工beanFactory");
    }
}

FactoryBean

FactoryBean和BeanFactory不是一個東西,大家不要混淆兩個概念,BeanFactory是管理我們注入的bean等,而FactoryBean本身也會被Spring管理,一旦Spring知道我們的bean實現了FactoryBean,那麼會自動呼叫getObject方法獲取我們自己建立的bean,這個bean完完全全交給我們自己建立了,我們可以這樣定義一個FactoryBean:


@Component
public class MyFactoryBean implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        UserService service = new UserService();
        return service;
    }

    @Override
    public Class<?> getObjectType() {
        return UserService.class;
    }

}

但是需要注意的是,這些注入UserService時,是不會有屬性依賴注入的,畢竟他沒有走bean的生命建立週期。細心的朋友會發現,這根我在設定類中寫@bean形式的類有啥區別,現象來講,他倆都可以被建立出來,但是值得一提的是,FactoryBean建立出來的bean是沒走spring定義的bean生命週期的。

MetadataReader、ClassMetadata、AnnotationMetadata

Spring啟動時需要掃描指定包路徑下的所有類檔案來獲取需要注入或管理的Bean資訊。然而,並非所有類都是需要的,這時可以使用ASM技術來解析類檔案的後設資料資訊,包括類上的註解資訊和類的基本資訊。ASM技術可以在執行時動態生成和修改Java位元組碼,從而高效地解析類檔案的後設資料資訊,避免了大量的IO操作和類載入,提高了應用的效能。以下是一個簡單的範例:

        SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();
        MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.xiaoyu.service.UserService");
        System.out.println(metadataReader.getClassMetadata().getClassName());
        metadataReader.getAnnotationMetadata().getAnnotationTypes().forEach(System.out::println);

結語

通過本文的解析,我們大致瞭解了Spring框架中的一些關鍵元件及其用途,這有助於我們在深入理解Spring原始碼過程中建立起一個整體框架。Spring原始碼量很大,要真正理解透徹還需要投入大量時間進行細緻學習和總結。但如果先對一些關鍵元件有一個大致的認識,有助於我們進行鍼對性學習,避免迷失在繁雜的細節中。希望本文能夠對讀者有一定的幫助,更希望讀者在學習Spring原始碼的過程中,不斷總結和提高,並在一定階段有所突破。祝讀者順利!
公眾號