Spring-Bean的生命週期

1. 前言

最初接觸java的接口服務是servlet,最基礎要掌握的就是servlet的生命週期。如今都是基於springboot開發後臺接口,spring框架的複雜度遠遠高於原始的servlet,弄清楚spring中bean的生命週期,有利於咱們對其框架的熟練掌握。java

2. spring容器加載

springboot的啓動類中,都會執行SpringApplication.run方法。在建立上下文環境以後,最核心的方法就是refreshContext,對上下文環境進行初始化操做,本段就着重說明這一過程。spring

2.1. refreshContext全過程

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {

            /**
             * 準備環境
             * 1. 記錄容器的啓動時間startupDate,標記容器爲激活
             * 2. 初始化上下文環境如文件路徑信息
             * 3. 驗證必填屬性是否填寫
             */
            prepareRefresh();

            /**
             * 告訴子類去刷新beanFactory
             * 這步完成後配置文件就解析成一個個beanDefination,註冊到BeanFactory(可是未被初始化,僅將信息寫到了beanDefination的map中)
             */
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            /**
             * 設置beanFactory類加載器,添加多個beanPostProcesser
             */
            prepareBeanFactory(beanFactory);

            try {
                /**
                 * 容許子類上下文中對beanFactory作後期處理
                 */
                postProcessBeanFactory(beanFactory);

                /**
                 * 執行 context 中註冊的 BeanFactoryPostProcessor中的postProcessBeanFactory() 方法
                 * 1. BeanFactoryPostProcessor 是 bean 屬性處理容器。即管理 bean工廠中的BeanDefinition
                 * 2. BeanDefinition在 spring mvc中就是xml文件中對應的bean標籤(注意,是標籤,而不是真實的 java bean)
                 */
                invokeBeanFactoryPostProcessors(beanFactory);

                /**
                 * 註冊 BeanPostProcessor 的實現類
                 * 1. BeanPostProcessor接口包含兩個方法:postProcessBeforeInitialization 和 postProcessAfterInitialization
                 * 2. 這裏只是註冊,兩個方法實際分別在 Bean初始化以前和初始化以後獲得執行
                 */
                registerBeanPostProcessors(beanFactory);

                /**
                 * 初始化ApplicationContext的MessageSource,MessageSource是國際化相關的接口
                 */
                initMessageSource();

                /**
                 * 初始化ApplicationContext事件廣播器
                 */
                initApplicationEventMulticaster();

                // 初始化子類特殊bean(鉤子方法)
                onRefresh();

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

                /**
                 * 初始化全部的單例java bean(不包含懶加載的類)
                 */
                finishBeanFactoryInitialization(beanFactory);

                /**
                 * 廣播事件,ApplicationContext初始化完成
                 */
                finishRefresh();
            } catch (BeansException ex) {
                ....................
            }

        }
    }

2.2. BeanDefinition

BeanDefinition 是 spring bean 的建模對象,即把一個bean實例化出來的模型對象。有人會問把一個bean實例化出來有Class就好了啊,Class也就是咱們一般說的類對象,就是一個普通對象的建模對象,那麼爲何spring不能用Class來創建bean呢?很簡單,由於Class沒法完成bean的抽象,好比bean的做用域,bean的注入模型,bean是不是懶加載等等信息,Class是沒法抽象出來的,故而須要一個BeanDefinition類來抽象這些信息,以便於spring可以完美的實例化一個bean。緩存

spring掃描到某個bean的類時,除了要存儲類名、構造器等Class的基礎信息之外,還要存儲scope,lazy,dependsOn等spring的屬性。BeanDefintion是一個類,專門用來存儲上述bean中的各類屬性。所以當spring掃描到一個符合規則的類時,首先是實例化一個BeanDefinition的對象,而且把根據類的類名生成一個bean的名字,繼而以beanName爲key,BeanDefinition對象爲value,存放在一個map中。springboot

值得注意的是,這裏都是講在掃描bean後,BeanDefinition的實例化,以及放入BeanDefinition的map中。尚未講到Bean的實例化和裝載。mybatis

BeanDefinition 的屬性方法以下:mvc

  • String: getBeanClassName: 返回當前bean definition定義的類名
  • ConstructorArgumentValues: getConstructorArgumentValues:返回bean的構造函數參數
  • String[]: getDependsOn:返回當前bean所依賴的其餘bean的名稱
  • String: getFactoryBeanName: 返回factory bean的名稱
  • String: getFactoryMethodName: 返回工廠方法的名稱
  • BeanDefinition: getOriginatingBeanDefinition: 返回原始的BeanDefinition,若是不存在返回null
  • String: getParentName: 返回當前bean definition的父definition的名字
  • MutablePropertyValues: getPropertyValues: 返回一個用於新的bean實例上的屬性值
  • String: getScope: 返回當前bean的目標範圍
  • boolean: isAbstract: 當前bean是不是abstract,意味着不能被實例化
  • boolean: isLazyInit: bean是不是延遲初始化
  • boolean: isPrimary: bean是否爲自動裝配的主要候選bean
  • boolean: isPrototype: bean是不是多實例
  • boolean: isSingleton: bean是不是單例
  • void: setAutowiredCandidate(boolean): 設置bean是否對其餘bean是自動裝配的候選bean
  • void: setBeanClassName(String): 指定bean definition的類名
  • void: setDependsOn(String ...): 設置當前bean初始化所依賴的beans的名稱
  • void: setFactoryBeanName(String): 若是factory bean的名稱
  • void: setFactoryMethodName(String): 設置工廠的方法名
  • void: setLazyInit(boolean lazyInit): 設置是否延遲初始化
  • void: setParentName(String): 設置父definition的名稱
  • void: setPrimary(boolean): 設置是否主要的候選bean
  • void: setScope(String): 設置bean的範圍,如:單例,多實例

2.3. BeanFactoryPostProcessor

BeanFactoryPostProcessor是實現spring容器功能擴展的重要接口,例如修改BeanDefinition,實現bean動態代理等。不少框架都是經過此接口實現對spring容器的擴展,例如mybatis與spring集成時,只定義了mapper接口,無實現類,接口也沒有添加@Component等註解,但spring卻能夠完成自動注入,也是基於BeanFactoryPostProcessor接口來實現的。app

BeanFactoryPostProcessor.java框架

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

前文說到BeanDefinition在實例化以後,被放入BeanDefinition map中,而BeanFactoryPostProcessor就是在BeanDefinition map初始化完成後,Bean實例構造方法執行以前執行的。ide

BeanFactoryPostProcessor的主要做用是,開發者能夠修改容器中全部BeanDefinition的信息,接口方法的入參是ConfigurrableListableBeanFactory,使用該參數,能夠獲取到相關bean的定義信息。但 絕對不容許進行bean實例化相關的操做!由於中spring加載機制中,BeanFactoryPostProcessor是在Bean構造方法以前執行的,若是這個時候提早實例化bean,極可能會一連串的問題。函數

如:在接口用經過configurableListableBeanFactory.getBean(「user」),獲取User類的bean,實際上即實例化了User的bean。會致使在後續其餘節點代碼中,沒法注入User的bean實例。

2.4. BeanPostProcessor

若是說BeanFactoryPostProcessor是對BeanDefinition的擴展處理,那麼BeanPostProcessor更多的是對Bean的擴展處理。BeanPostProcessor的觸發時間是在 Bean的實例化以後(執行了構造方法,而且對屬性進行了賦值),在Bean的init方法執行以前(@PostConstruct註解方法、InitializeBean接口方法、@Bean中initMethod方法)

BeanPostProcessor.java

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

BeanPostProcessor接口中定義兩個方法,根據名字能夠推測,postProcessBeforeInitialization在Bean的init方法以前執行,postProcessAfterInitialization在Bean的init方法以後執行。這點很好的體現了spring的AOP思想,能夠理解爲BeanPostProcessor是對Bean的init方法執行先後,作了一層切面攔截。

有一個實現Spring aop 的BeanPostProcessor 叫 AnnotationAwareAspectJAutoProxyCreator。在postProcessBeforeInitialization或者postProcessAfterInitialization方法中,對對象進行判斷,看他需不須要織入切面邏輯,若是須要,那我就根據這個對象,生成一個代理對象,而後返回這個代理對象,那麼最終注入容器的,天然就是代理對象了。

2.5. finishBeanFactoryInitialization

該方法會實例化全部剩餘的非懶加載單例 bean。值得注意的是「全部剩餘的」,就是有一些特殊的bean,這該方法以前就已經加載了,如:

  1. 一些spring容器內部的bean;
  2. 實現了 BeanFactoryPostProcessor 接口的bean;
  3. 實現了 BeanPostProcessor 接口的 bean(可是這些bean會在這個方法中觸發)。

其餘的非懶加載單例 bean 都會在這個方法中被實例化,而且 BeanPostProcessor 的觸發也是在這個方法中。

3. Bean的生命履歷

基於spring的refresh過程,咱們把關於bean的部分拎出來,就造成了下面的bean的加載過程:

  1. spring基於@ComponentScan等註解掃描到bean,並將每一個bean實例化成包含bean各類屬性的BeanDefinition的對象,放入beanDefinitionMap中。
  2. spring 中的 bean 簡單分爲:特殊bean和普通bean,beanFactory實際會先實例化特殊bean。
  3. 實現BeanFactoryPostProcessor接口的就是一種特殊bean,在beanDefinitionMap加載後觸發,能夠自定義實現類,對其中的BeanDefinition進行修改。
  4. construct過程。BeanFactory 執行getBean方法生產其餘的普通bean(調用類的構造方法,或FactoryBean的getObject方法,以及@Bean註解的方法)。
  5. 此時bean獲取會受三級緩存(singletonFactories、earlySingletonObjects、singletonObjects)影響,如earlySingletonObjects會提早曝光還沒有populate屬性數據的單例對象,可解決循環依賴問題。
  6. populate過程。設置bean的依賴關係(基於屬性注入)以及屬性值。
  7. 對於實現BeanPostProcessor接口的類,執行接口中的postProcessBeforeInitialization 方法。
  8. initialze過程。執行bean的各類初始化方法,initialze方法的優先級以下:(1)@PostConstruct指定的方法 -> (2)InitializingBean接口的afterPropertiesSet()方法 -> (3)@Bean中initMethod指定的方法
  9. 對於實現BeanPostProcessor接口的類,執行接口中的postProcessAfterInitialization 方法。
  10. destroy過程,容器銷燬,執行bean的各類銷燬方法,destroy方法的優先級以下:(1)@PostDestroy指定的方法 -> (2)DisposableBean接口的destroy()方法 -> (3)@Bean中destroyMethod指定的方法

在spring bean生命週期中,網上有人用Bean的「初始化」、「實例化」等名字,很容易搞混,因此我使用了下列幾個英文單詞,感受更能表達這些過程:

  • construct:是對象建立的過程。好比使用構造方法new對象。
  • populate:是爲對象中的屬性賦值的過程,包括@Autowired等依賴注入,@Value的賦值等。
  • initialize:執行初始化方法。

其實並不是只有在populate時纔會注入依賴,仍是要取決於屬性的注入方式:基於屬性注入、setter注入和構造器注入等。若是是基於構造器注入,在construct過程當中,就會導入依賴。

另外咱們中使用@Value、@ConfigurationProperties等註解賦值,也是屬於populate過程的操做。

四、bean相關方法

對於上述關於bean生命週期的介紹,咱們寫個demo,一一實現相關的方法,親眼看看他們的加載過程。

4.一、BeanFactoryPostProcessor

@Slf4j
@Component
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {

    /**
     * getBean 是獲取Object對象,getBeanDefinition 是獲取Class類
     * 能夠修改BeanDefinition
     * 但getBean方法不要用,會致使提早加載bean,bean實例化紊亂
     * @param beanFactory
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        log.warn("實現 BeanFactoryPostProcessor 接口:開始了!");
        Arrays.stream(beanFactory.getBeanDefinitionNames())
                .filter(beanName -> User.USER_BEAN_NAME.equals(beanName))
                .forEach(beanName -> {
                    BeanDefinition beanDefinition=beanFactory.getBeanDefinition(beanName);
                    beanDefinition.getPropertyValues().add("name","Kerry");
                    log.warn("BeanFactoryPostProcessor:Bean name is " + beanName);
                });
    }
}

主要是對 BeanDefinitionName 的修改,這裏將對象的屬性name的值設置爲Kerry。

4.二、BeanPostProcessor

@Slf4j
@Component
public class BeanPostProcessorImpl implements BeanPostProcessor {

    /**
     * Bean 初始化方法 執行前 調用
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(User.USER_BEAN_NAME.equals(beanName)) {
            log.warn("BeanPostProcessor-name:"+((User)bean).getName());
            log.warn("實現 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=" + beanName);
        }
        return bean;
    }

    /**
     * Bean 初始化方法 執行後 調用
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(User.USER_BEAN_NAME.equals(beanName)) {
            log.warn("實現 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=" + beanName);
        }
        return bean;
    }
}

4.三、初始化和銷燬方法

初始化和銷燬兩種方法每每是配套的,有下面三種,而且按照下列順序執行:

  1. @PostConstruct 和 @PreDestroy 註解修飾的方法,分別在初始化和銷燬時執行。
  2. 實現 InitializeBean 接口的 afterPropertiesSet()方法,和實現 DisposableBean 接口的 destroy()方法,分別在初始化和銷燬時執行。
  3. @Bean 中initMethod屬性指定的方法,和destroyMethod屬性指定的方法,分別在初始化和銷燬時執行。

咱們在User類中定義了這些初始化和銷燬方法,因爲還要測試依賴注入順序,也定義了Type類的方法。

User.java

@Slf4j
public class User implements InitializingBean, DisposableBean {
    public final static String USER_BEAN_NAME="user";
    //配置文件中,注入的值爲 demoName
    @Value("${params.user.name}")
    private String name;
    private Integer age=1;

    @Autowired
    private Type type;


    @Override
    public String toString(){
        return "My name is "+name+",I'm "+age+"years old.";
    }

    /**
     * 構造方法
     * 一、默認構造方法
     * 二、重載構造方法
     */
    public User(){
        log.warn("構造方法:public User()");
    }

    /**
     * 初始化
     * 一、註解:@PostConstruct 的方法
     * 二、實現 InitializeBean 接口:afterPropertiesSet()
     * 三、@Bean 中指定的 initMethod
     */
    @PostConstruct
    public void postConstruct(){
        log.warn("註解:@PostConstruct 的方法");
    }

    @Override
    public void afterPropertiesSet(){
        log.warn("實現 InitializeBean 接口:afterPropertiesSet()");
    }

    public void initMethod(){
        log.warn("@Bean 中指定的 initMethod ");
    }

    /**
     * 銷燬
     * 一、註解:@PreDestroy 的方法
     * 二、實現 DisposableBean 接口:destroy()
     * 三、@Bean 中指定的 destroyMethod
     */
    @PreDestroy
    public void preDestroy(){
        log.warn("註解:@PreDestroy 的方法");
    }

    @Override
    public void destroy(){
        log.warn("實現 DisposableBean 接口:destroy()");
    }

    public void destroyMethod(){
        log.warn("@Bean 中指定的 destroyMethod");
    }
    
    public void setName(String name) {
        log.warn("User.setName方法:"+name);
        this.name = name;
    }
}

Type.java

@Component
@Data
@Slf4j
public class Type implements InitializingBean {
    private String typeCode;

    public Type(){
       log.warn("構造方法:public Type()");
    }

    @Override
    public void afterPropertiesSet(){
        log.warn("Type:實現 InitializeBean 接口:afterPropertiesSet()");
    }
}

BeanConfig.java

@Configuration
public class BeanConfig {
    
    @Bean(initMethod = "initMethod",destroyMethod = "destroyMethod")
    public User user(){
        return new User();
    }
}

4.4. 結果驗證

還記得咱們在BeanFactoryPostProcessorImpl中修改了name的值嗎?咱們先把修改的代碼註釋掉,執行以後的日誌以下,的確按照前面的Bean生命週期加載的:

p.k.e.s.c.BeanFactoryPostProcessorImpl   : 實現 BeanFactoryPostProcessor 接口:開始了!
p.k.e.s.c.BeanFactoryPostProcessorImpl   : BeanFactoryPostProcessor:Bean name is user
p.k.exercise.springexercise.pojo.User    : 構造方法:public User()
p.k.exercise.springexercise.pojo.Type    : 構造方法:public Type()
p.k.exercise.springexercise.pojo.Type    : Type:實現 InitializeBean 接口:afterPropertiesSet()
p.k.e.s.config.BeanPostProcessorImpl     : BeanPostProcessor-name:demoName
p.k.e.s.config.BeanPostProcessorImpl     : 實現 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User    : 註解:@PostConstruct 的方法
p.k.exercise.springexercise.pojo.User    : 實現 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User    : @Bean 中指定的 initMethod 
p.k.e.s.config.BeanPostProcessorImpl     : 實現 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User    : 註解:@PreDestroy 的方法
p.k.exercise.springexercise.pojo.User    : 實現 DisposableBean 接口:destroy()
p.k.exercise.springexercise.pojo.User    : @Bean 中指定的 destroyMethod

如今把BeanFactoryPostProcessorImpl中的註釋放開,日誌變了:

p.k.e.s.c.BeanFactoryPostProcessorImpl   : 實現 BeanFactoryPostProcessor 接口:開始了!
p.k.e.s.c.BeanFactoryPostProcessorImpl   : BeanFactoryPostProcessor:Bean name is user
p.k.exercise.springexercise.pojo.User    : 構造方法:public User()
p.k.exercise.springexercise.pojo.Type    : 構造方法:public Type()
p.k.exercise.springexercise.pojo.Type    : Type:實現 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User    : User.setName方法:Kerry
p.k.e.s.config.BeanPostProcessorImpl     : BeanPostProcessor-name:Kerry
p.k.e.s.config.BeanPostProcessorImpl     : 實現 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User    : 註解:@PostConstruct 的方法
p.k.exercise.springexercise.pojo.User    : 實現 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User    : @Bean 中指定的 initMethod 
p.k.e.s.config.BeanPostProcessorImpl     : 實現 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User    : 註解:@PreDestroy 的方法
p.k.exercise.springexercise.pojo.User    : 實現 DisposableBean 接口:destroy()
p.k.exercise.springexercise.pojo.User    : @Bean 中指定的 destroyMethod

能夠看到若是咱們在BeanFactoryPostProcessor接口中經過BeanDefinition 的PropertyValue 設置屬性值,其實只是修改了該屬性的setter方法,而真正執行該方法,仍是在populate階段,而非BeanFactoryPostProcessor執行階段。

相關文章
相關標籤/搜索