天天看點

SpringBoot 啟動流程(三):Bean的生命周期一、Bean的生命周期原理二、代碼驗證實作三 源碼分析

文章目錄

  • 一、Bean的生命周期原理
  • 二、代碼驗證實作
    • 2.1 定義測試用的實體類
    • 2.2 以java注解的方式注入Person
    • 2.3 定義BeanPostProcessor的實作類
    • 2.4 啟動類
    • 2.5 測試結果
  • 三 源碼分析

本文基于以下版本驗證:

項目 版本 下載下傳位址
SpringBoot 2.0.4release https://github.com/spring-projects/spring-boot
SpringFramework 5.0.8.release https://github.com/spring-projects/spring-framework

一、Bean的生命周期原理

以下内容摘自Spring Bean的生命周期

Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能夠精确地知道該 Bean 何時被建立,何時初始化完成,以及何時被銷毀。

而對于 prototype 作用域的 Bean,Spring 隻負責建立,當容器建立了 Bean 的執行個體後,Bean 的執行個體就交給用戶端代碼管理,Spring 容器将不再跟蹤其生命周期。每次用戶端請求 prototype 作用域的 Bean 時,Spring 容器都會建立一個新的執行個體,并且不會管那些被配置成 prototype 作用域的 Bean 的生命周期。

了解 Spring 生命周期的意義就在于,可以利用 Bean 在其存活期間的指定時刻完成一些相關操作。這種時刻可能有很多,但一般情況下,會在 Bean 被初始化後和被銷毀前執行一些相關操作。

在 Spring 中,Bean 的生命周期是一個很複雜的執行過程,我們可以利用 Spring 提供的方法定制 Bean 的建立過程。

當一個 Bean 被加載到 Spring 容器時,它就具有了生命,而 Spring 容器在保證一個 Bean 能夠使用之前,會進行很多工作。Spring 容器中 Bean 的生命周期流程如圖 1 所示。

SpringBoot 啟動流程(三):Bean的生命周期一、Bean的生命周期原理二、代碼驗證實作三 源碼分析

圖 1 Bean 的生命周期的整個執行過程描述如下。

1)根據配置情況調用 Bean 構造方法或工廠方法執行個體化 Bean。

2)利用依賴注入完成 Bean 中所有屬性值的配置注入。

3)如果 Bean 實作了 BeanNameAware 接口,則 Spring 調用 Bean 的 setBeanName() 方法傳入目前 Bean 的 id 值。

4)如果 Bean 實作了 BeanFactoryAware 接口,則 Spring 調用 setBeanFactory() 方法傳入目前工廠執行個體的引用。

5)如果 Bean 實作了 ApplicationContextAware 接口,則 Spring 調用 setApplicationContext() 方法傳入目前 ApplicationContext 執行個體的引用。

6)如果 BeanPostProcessor 和 Bean 關聯,則 Spring 将調用該接口的預初始化方法 postProcessBeforeInitialzation() 對 Bean 進行加工操作,此處非常重要,Spring 的 AOP 就是利用它實作的。

7)如果 Bean 實作了 InitializingBean 接口,則 Spring 将調用 afterPropertiesSet() 方法。

8)如果在配置檔案中通過 init-method 屬性指定了初始化方法,則調用該初始化方法。

9)如果 BeanPostProcessor 和 Bean 關聯,則 Spring 将調用該接口的初始化方法 postProcessAfterInitialization()。此時,Bean 已經可以被應用系統使用了。

10)如果在 中指定了該 Bean 的作用範圍為 scope=“singleton”,則将該 Bean 放入 Spring IoC 的緩存池中,将觸發 Spring 對該 Bean 的生命周期管理;如果在 中指定了該 Bean 的作用範圍為 scope=“prototype”,則将該 Bean 交給調用者,調用者管理該 Bean 的生命周期,Spring 不再管理該 Bean。

11)如果 Bean 實作了 DisposableBean 接口,則 Spring 會調用 destory() 方法将 Spring 中的 Bean 銷毀;如果在配置檔案中通過 destory-method 屬性指定了 Bean 的銷毀方法,則 Spring 将調用該方法對 Bean 進行銷毀。

Spring 為 Bean 提供了細緻全面的生命周期過程,通過實作特定的接口或 的屬性設定,都可以對 Bean 的生命周期過程産生影響。雖然可以随意配置 的屬性,但是建議不要過多地使用 Bean 實作接口,因為這樣會導緻代碼和 Spring 的聚合過于緊密。

二、代碼驗證實作

2.1 定義測試用的實體類

package org.springframework.boot.demo.domain;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

//配置自動注入的注解
@Component
public class Person implements BeanFactoryAware, BeanNameAware ,ApplicationContextAware,
		InitializingBean, DisposableBean {

	private String name;
	private String address;
	private int phone;

	private BeanFactory beanFactory;
	private String beanName;
	private ApplicationContext applicationContext;

	public Person() {
		System.out.println("【構造器】調用Person的構造器執行個體化");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		System.out.println("【注入屬性】注入屬性name");
		this.name = name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		System.out.println("【注入屬性】注入屬性address");
		this.address = address;
	}

	public int getPhone() {
		return phone;
	}

	public void setPhone(int phone) {
		System.out.println("【注入屬性】注入屬性phone");
		this.phone = phone;
	}

	@Override
	public String toString() {
		return "Person [address=" + address + ", name=" + name + ", phone="
				+ phone + "]";
	}

	// 這是BeanFactoryAware接口方法
	@Override
	public void setBeanFactory(BeanFactory arg0) throws BeansException {
		System.out
				.println("【BeanFactoryAware接口】調用BeanFactoryAware.setBeanFactory()");
		this.beanFactory = arg0;
	}

	@Override
	public void setApplicationContext(ApplicationContext arg0) throws BeansException{
		System.out.println("【ApplicationContextAware接口】調用ApplicationContextAware.setApplicationContext()");
		this.applicationContext=arg0;
	}

	// 這是BeanNameAware接口方法
	@Override
	public void setBeanName(String arg0) {
		System.out.println("【BeanNameAware接口】調用BeanNameAware.setBeanName()");
		this.beanName = arg0;
	}

	@PostConstruct
	public void postConstruct(){
		System.out.println("調用Bean的函數(postConstruct)");
	}

	// 這是InitializingBean接口方法
	@Override
	public void afterPropertiesSet() throws Exception {
		System.out
				.println("【InitializingBean接口】調用InitializingBean.afterPropertiesSet()");
	}

	// 通過<bean>的init-method屬性指定的初始化方法
	public void myInit() {
		System.out.println("【init-method】調用<bean>的init-method屬性指定的初始化方法");
	}

	@PreDestroy
	public void preDestroy(){
		System.out.println("調用Bean的函數(preDestroy)");
	}

	//DisposableBean接口的方法destroy
	@Override
	public void destroy() throws Exception {
		System.out.println("調用Bean的函數(destroy)");
	}

	// 通過<bean>的destroy-method屬性指定的初始化方法
	public void myDestory() {
		System.out.println("【destroy-method】調用<bean>的destroy-method屬性指定的初始化方法");
	}
}

           

2.2 以java注解的方式注入Person

一些參考資料,多使用xml配置的方式(在SpringMVC時流行),這裡采用注解的方式。

package org.springframework.boot.demo.config;

import org.springframework.boot.demo.domain.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TestConfig {
	@Bean(initMethod = "myInit",destroyMethod = "myDestory")
	public Person person(){
		Person person =new Person();
		person.setAddress("廣州");
		person.setName("張三");
		return person;
	}
}

           

2.3 定義BeanPostProcessor的實作類

package org.springframework.boot.demo.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.demo.domain.Person;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
	public MyBeanPostProcessor() {
		super();
		System.out.println("這是BeanPostProcessor實作類構造器!!");
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		//postProcessAfterInitialization方法在初始化過程中,會被調用多次,加上該判斷,表示隻對指定class進行處理。
		if(bean.getClass()==Person.class) {
			System.out
					.println("BeanPostProcessor接口方法postProcessAfterInitialization對屬性進行更改!");
		}

		return bean;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if(bean.getClass()==Person.class) {
			System.out
					.println("BeanPostProcessor接口方法postProcessBeforeInitialization對屬性進行更改!");
		}

		return bean;
	}
}

           

2.4 啟動類

package org.springframework.boot.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.demo.domain.Person;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MySpringBootApplication {
	public static void main(String[] args) {
		SpringApplication.run(MySpringBootApplication.class, args);
	}
}
           

2.5 測試結果

【構造器】調用Person的構造器執行個體化
【注入屬性】注入屬性address
【注入屬性】注入屬性name
【BeanNameAware接口】調用BeanNameAware.setBeanName()
【BeanFactoryAware接口】調用BeanFactoryAware.setBeanFactory()
【ApplicationContextAware接口】調用ApplicationContextAware.setApplicationContext()
BeanPostProcessor接口方法postProcessBeforeInitialization對屬性進行更改!
調用Bean的函數(postConstruct)
【InitializingBean接口】調用InitializingBean.afterPropertiesSet()
【init-method】調用<bean>的init-method屬性指定的初始化方法
BeanPostProcessor接口方法postProcessAfterInitialization對屬性進行更改!
--- --- 
調用Bean的函數(preDestroy)
調用Bean的函數(destroy)
【destroy-method】調用<bean>的destroy-method屬性指定的初始化方法
Process finished with exit code 1
           

三 源碼分析

SpringBoot 啟動流程(三):Bean的生命周期一、Bean的生命周期原理二、代碼驗證實作三 源碼分析

源碼圖比較粗糙,暫用于個人筆記積累,源碼部分,後面抽時間陸續完善UML圖和原了解析。大緻記錄下涉及到的幾個必要重要的方法:

finishBeanFactoryInitialization 進行初始化

instantiatebean 初始化bean,調用bean的構造器

invokeAwareMethod 調用bean的aware方法

文章參考:以下文章非常好,如果讀到這裡,也推薦看一看

Spring Boot Bean生命周期 https://www.jianshu.com/p/8e2d400492c7

Spring Bean的生命周期 http://c.biancheng.net/view/4261.html

Spring:源碼解讀Spring IOC原理 https://www.cnblogs.com/ITtangtang/p/3978349.html

Spring Bean的生命周期(非常詳細)https://www.cnblogs.com/zrtqsk/p/3735273.html

Spring IOC 容器源碼分析 https://javadoop.com/post/spring-ioc#toc_6