淺嘗Spring註解開發_Bean生命週期及執行過程

蔚然丶丶發表於2022-04-30

Spring註解開發

淺嘗Spring註解開發,基於Spring 4.3.12
包含Bean生命週期、自定義初始化方法、Debug BeanPostProcessor執行過程及在Spring底層中的應用

淺嘗Spring註解開發_自定義註冊元件、屬性賦值、自動裝配
淺嘗Spring註解開發_Bean生命週期及執行過程

Bean生命週期

瞭解Bean的生命週期,就可以在Bean宣告週期的不同階段進行自定義的操作,滿足更復雜的需求。簡單的將Bean生命週期分為三個階段:Bean建立、初始化、銷燬

  • 物件建立:單例項在容器啟動的時候建立物件,多例項在每次獲取的時候建立物件
    • 初始化之前:BeanPostProcessor.postProcessBeforeInitialization()
  • 初始化:物件建立完成,並賦值好,呼叫初始化方法
    • 初始化之後:BeanPostProcessor.postProcessAfterInitialization()
  • [容器建立完成]
  • 銷燬:單例項在容器關閉的時候銷燬,多例項容器不會管理這個bean,容器不會呼叫銷燬方法

現在可以通過下面方法在初始化和銷燬時自定義初始化方法來干涉Bean建立過程。

  1. @Bean()註解引數
  2. InitializingBean、DisposableBean介面
  3. @PostConstruct、@PreDestroy註解
  4. BeanPostProcessor介面

1.@Bean生命週期

通過@Bean指定init-method和destroy-method的初始化方法

  • 先自定義Bean初始化和銷燬方法

    @Component
    public class Car {
    	
    	public Car(){
    		System.out.println("car constructor...");
    	}
    	//現在只是普通方法
    	public void init(){
    		System.out.println("car ... init...");
    	}
    	//現在只是普通方法
    	public void detory(){
    		System.out.println("car ... destory...");
    	}
    
    }
    
  • 配置進容器

    • 通過@Bean註解,在@Bean註冊進容器時指定自定義方法
    @Configuration
    public class MainConfigOfLifeCycle {
    	
    	//@Scope("prototype")多例項,不管銷燬
    	//指定用於初始化和銷燬的方法
    	@Bean(initMethod="init",destroyMethod="destory")
    	public Car car(){
    		return new Car();
    	}
    
    }
    
  • 測試

    public class IOCTest_LifeCycle {
    	
    	@Test
    	public void test01(){
    		//1、建立ioc容器
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    		System.out.println("容器建立完成...");
    		
    		//applicationContext.getBean("car");
    		//關閉容器
    		applicationContext.close();
    	}
    
    }
    

    輸出

    //先建立物件
    car constructor...
    //再自定義初始化方法
    car ... init...
    //建立完成
    容器建立完成...
    //關閉時自定義銷燬方法
    car ... destory...
    

2.InitializingBean,DisposableBean生命週期

介面,需實現,通過讓Bean實現InitializingBean(定義初始化邏輯),DisposableBean(定義銷燬邏輯);

  • 實現介面,自定義初始化Bean

    public class Cat implements InitializingBean,DisposableBean {
    	
    	public Cat(){
    		System.out.println("cat constructor...");
    	}
    
    	//定義銷燬邏輯
    	@Override
    	public void destroy() throws Exception {
    		// TODO Auto-generated method stub
    		System.out.println("cat...destroy...");
    	}
    
    	//定義初始化邏輯
    	@Override
    	public void afterPropertiesSet() throws Exception {
    		// TODO Auto-generated method stub
    		System.out.println("cat...afterPropertiesSet...");
    	}
    
    }
    
  • 配置進容器

    • 在@Configuration配置類中使用@Bean
    • 或在Bean類上使用@Component然後再配置類上使用@ComponentScan
    //配置元件
    @Component
    public class Cat implements InitializingBean,DisposableBean {
    	//...
    }
    
    //掃描進容器
    @ComponentScan("com.xxx.bean")
    @Configuration
    public class MainConfigOfLifeCycle {
    	//...
    }
    
  • 測試

    public class IOCTest_LifeCycle {
    	
    	@Test
    	public void test01(){
    		//1、建立ioc容器
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    		System.out.println("容器建立完成...");
    		
    		//applicationContext.getBean("car");
    		//關閉容器
    		applicationContext.close();
    	}
    }
    

    輸出

    //注意順序,每個Bean先構造並初始化,然後才進行下一個Bean,關閉時從內向外
    (貓)cat constructor...
    (貓)cat...afterPropertiesSet...
    (車)car constructor...
    (車)car ... init...
    //建立完成
     容器建立完成...
    //關閉時銷燬
    (車)car ... destory...
    (貓)cat...destroy...
    

3.@PostConstruct生命週期

可以使用JSR250;

  • @PostConstruct:在bean建立完成並且屬性賦值完成之後,來執行初始化方法

  • @PreDestroy:在容器銷燬bean之前通知我們進行清理工作

  • 標註註解,自定義初始化Bean

    public class Dog {
    
    	public Dog(){
    		System.out.println("dog constructor...");
    	}
    	
    	//物件建立並賦值之後呼叫
    	@PostConstruct
    	public void init(){
    		System.out.println("Dog....@PostConstruct...");
    	}
    	
    	//容器移除物件之前
    	@PreDestroy
    	public void detory(){
    		System.out.println("Dog....@PreDestroy...");
    	}
    }
    
  • 配置進容器

    • 在@Configuration配置類中使用@Bean
    • 或在Bean類上使用@Component然後再配置類上使用@ComponentScan
    @Component
    public class Dog {
    	//...
    }
    
    //掃描進容器
    @ComponentScan("com.xxx.bean")
    @Configuration
    public class MainConfigOfLifeCycle {
    	//...
    }
    
  • 測試

    public class IOCTest_LifeCycle {
    	
    	@Test
    	public void test01(){
    		//1、建立ioc容器
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    		System.out.println("容器建立完成...");
    		
    		//applicationContext.getBean("car");
    		//關閉容器
    		applicationContext.close();
    	}
    
    }
    

    輸出

    //注意順序,每個Bean先構造並初始化,然後才進行下一個Bean,關閉時從內向外
    (貓)cat constructor...
    (貓)cat...afterPropertiesSet...
    (狗)dog constructor...
    (狗)Dog....@PostConstruct...
    (車)car constructor...
    (車)car ... init...
    //建立完成
    容器建立完成...
    //關閉時銷燬
    (車)car ... destory...
    (狗)Dog....@PreDestroy...
    (貓)cat...destroy...
    

4.BeanPostProcessor

postProcessBeforeInitialization:在建立Bean例項之後,在自定義初始化之前進行呼叫

postProcessAfterInitialization:在自定義初始化之後進行呼叫

BeanPostProcessor介面:bean的後置處理器,需實現,在bean初始化前後進行一些處理工作

  • postProcessBeforeInitialization:(自定義初始化,如InitializingBean[afterPropertiesSet]、init-method等,就是上面那些自定義初始化方法)初始化之前工作(建立Bean例項之後,在自定義初始化之前)

  • postProcessAfterInitialization:在(自定義)初始化之後工作

  • 實現後置處理器介面

    public class MyBeanPostProcessor implements BeanPostProcessor {
    
    	//初始化前置方法
    	//bean:新建立的例項,還未初始化
    	//beanName:未初始化的Bean名字
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		// TODO Auto-generated method stub
    		System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);
    		return bean;
    	}
    	//初始化後置方法
    	@Override
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		// TODO Auto-generated method stub
    		System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean);
    		return bean;
    	}
        
    }
    
  • 配置進容器

    • 在@Configuration配置類中使用@Bean
    • 或在Bean類上使用@Component然後再配置類上使用@ComponentScan
    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        //...
    }
    
    //掃描進容器
    @ComponentScan("com.xxx.bean")
    @Configuration
    public class MainConfigOfLifeCycle {
    	//...
    }
    
  • 測試

    • 這次沒有新增的Bean,只配置了一個後置處理器,
    • 這個後置處理器會對容器中的Bean起作用,包括上面三種自定義初始化Bean
    public class IOCTest_LifeCycle {
    	
    	@Test
    	public void test01(){
    		//1、建立ioc容器
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    		System.out.println("容器建立完成...");
    		
    		//applicationContext.getBean("car");
    		//關閉容器
    		applicationContext.close();
    	}
    
    }
    

    輸出

    //對於每一個Bean都要執行一遍
    //1.建立
    //2.BeanPostProcessor.postProcessBeforeInitialization()
    //3.初始化:物件建立完成,並賦值好,呼叫初始化方法...
    //4.BeanPostProcessor.postProcessAfterInitialization()
    //5.銷燬
    
    
    //以其中一個Bean為例:
    //構造物件
    cat constructor...
    //初始化之前
    postProcessBeforeInitialization...cat=>com.xxx.bean.Cat@7d68ef40
    //使用InitializingBean自定義初始化邏輯
    cat...afterPropertiesSet...
    //初始化之後
    postProcessAfterInitialization...cat=>com.xxx.bean.Cat@7d68ef40
    //建立完成
    容器建立完成...
    //關閉時銷燬
    cat ... destroy...
    

⭐BeanPostProcessor原理

bean賦值,注入其他元件,@Autowired,生命週期註解功能,@Async,xxxBeanPostProcessor都通過BeanPostProcessor實現

詳細視訊 https://www.bilibili.com/video/BV1gW411W7wy?p=16

主要方法

populateBean(beanName, mbd, instanceWrapper):給bean進行屬性賦值
initializeBean:初始化Bean
{
    applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);初始化前應用後置處理器
    invokeInitMethods(beanName, wrappedBean, mbd);執行自定義初始化
    applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);初始化後應用後置處理器
}
遍歷得到容器中所有的BeanPostProcessor;挨個執行beforeInitialization,
一但返回null,跳出for迴圈

執行過程

瞭解BeanPostProcessor的執行過程,從AnnotationConfigApplicationContext開始Debug

public class IOCTest_LifeCycle {
	
	@Test
	public void test01(){
		//1、建立ioc容器
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
		System.out.println("容器建立完成...");
		
		//applicationContext.getBean("car");
		//關閉容器
		applicationContext.close();
	}
}
  1. 先從建立ioc容器開始,進入AnnotationConfigApplicationContext()構造方法,執行裡面的refresh()方法重新整理容器
    1. refresh()方法裡面有一個finishBeanFactoryInitialization(beanFactory)初始化所有剩餘的單例項物件,進入這個方法
  2. 這個方法最後一步有一個beanFactory.preInstantiateSingletons()初始化所有單例項Bean,進入這個方法
    1. 觸發所有非惰性單例bean的初始化
    2. 裡面呼叫getBean(beanName)
    3. 進入getBean(beanName)裡面再呼叫doGetBean(name,null,null,false)
    4. 進入doGetBean(name,null,null,false)裡面有getSingleton(beanName,new ObjectFactory(){singletonFactory.getObject()})通過匿名內部類呼叫getObject()
  3. 此時通過匿名類getObject()進入下一個呼叫棧AbstractBeanFactory$1.getObject(),如果是單例,呼叫createBean(beanName,mbd,args)
  4. 進入createBean(beanName,mbd,args)呼叫doCreateBean(beanName,mbd,args)建立一個例項,過程如下
    1. 進入doCreateBean(beanName,mbd,args),裡面呼叫一個initializeBean(beanName,exposedObject,mbd)初始化方法,這個方法裡面就是呼叫的後置處理器
    2. 在這個方法上面有populateBean(beanName,mbd,instanceWrapper)方法,這個方法為Bean屬性賦值
    3. 進入initializeBean(beanName,exposedObject,mbd),下面有一個invokeInitMethods(beanName,wrappedBean,mbd)執行初始化方法(就是上面的自定義初始化InitializingBean[afterPropertiesSet]、init-method)
    4. invokeInitMethods(beanName,wrappedBean,mbd)[在初始化之前應用 BeanPost 處理器]上面有一個applyBeanPostProcessorsBeforeInitialization(wrappedBean,beanName)下面有一個applyBeanPostProcessorsAfterInitialization(wrappedBean,beanName)[在初始化之後應用 BeanPost 處理器],作用是在初始化之前應用所有的BeanPostProcessors在初始化之後應用所有的BeanPostProcessors
  5. 進入applyBeanPostProcessorsBeforeInitialization(wrappedBean,beanName)
    1. 裡面有getBeanPostProcessors()找到所有BeanPostProcessors遍歷,包括Spring系統的BeanPostProcessorApplicationContextAwareProcessorConfigurationClassPostProcessor等,然後才是自定義的MyBeanPostProcessor,依次執行beanProcessor.postProcessBeforeInitialization()
    2. 如果有執行返回null,就結束遍歷,返回null,後面的處理器就不執行了(不應用後續的BeanPostProcessors了)

呼叫棧

  • 獲取單例

    image

  • 建立例項Bean

    image

  • 給Bean屬性賦值和初始化Bean

    image

完整流程

Spring底層對 BeanPostProcessor 的使用;

image

  1. 由上圖可以看到,Spring中的BeanPostProcessor在例項化過程處於的位置,BeanPostProcessor介面有兩個方法需要實現:postProcessBeforeInitialization和postProcessAfterInitialization
  2. 前者在例項化及依賴注入完成後、在任何初始化程式碼(比如配置檔案中的init-method)呼叫之前呼叫;後者在初始化程式碼呼叫之後呼叫。

⭐BeanPostProcessor在Spring底層的使用

許多註解底層都是基於BeanPostProcessor

BeanPostProcessor介面實現類

image

向元件中注入IoC容器

在Bean建立過程中,初始化之前,判斷是否實現了某Aware介面,如果實現了,就向Bean中注入ApplicationContext容器

  • 向Bean中注入IoC容器

    • 實現ApplicationContextAware介面,宣告屬性,賦值,就可以在元件中使用Ioc容器

      @Component
      public class Dog implements ApplicationContextAware {
      	
      	//宣告IoC容器
      	private ApplicationContext applicationContext;
      	
      	public Dog(){
      		System.out.println("dog constructor...");
      	}
      	
      	//物件建立並賦值之後呼叫
      	@PostConstruct
      	public void init(){
      		System.out.println("Dog....@PostConstruct...");
      	}
      	
      	//容器移除物件之前
      	@PreDestroy
      	public void detory(){
      		System.out.println("Dog....@PreDestroy...");
      	}
      
      	//把applicationContext IoC容器賦值給屬性
      	@Override
      	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      		// TODO Auto-generated method stub
      		this.applicationContext = applicationContext;
      	}
      }
      
  • 原理是通過ApplicationContextAwareProcessor實現

    • ApplicationContextAwareProcessor實現了BeanPostProcessor介面

      image

    • postProcessBeforeInitialization()方法中

      1. 在Bean初始化之前,判斷Bean是否實現了ApplicationContextAware介面,或其他Aware介面
      2. 如果實現了,就呼叫invokeAwareInterfaces(bean)給Bean注入值
      3. 判斷Bean是什麼型別Aware,將Bean轉成對應型別呼叫((ApplicationContextAware)bean).setApplicationContext(this.applicationContext)注入IoC容器
      4. 於是就到了上面實現的介面的未實現方法中

資料校驗

  • BeanValidationPostProcessor也實現了BeanPostProcessor介面
  • 在Bean建立完賦值後,同樣呼叫postProcessBeforeInitialization()方法,進行資料校驗
    • postProcessBeforeInitialization(){doValidate(bean)}
    • postProcessAfterInitialization(){doValidate(bean)}

自定義初始化註解

  • Bean初始化有一種方法是使用@PostConstruct註解,也是通過BeanPostProcessor實現
  • InitDestroyAnnotationBeanPostProcessor處理@PostConstruct@PreDestroy註解
    • postProcessBeforeInitialization()中找到Bean的生命週期註解所標註的方法,如initMethods、destroyMethods
    • 找到之後就執行註解標註的初始化方法metatata.invokeInitMethods(bean,beanName)element.invoke(target),利用反射執行。

自動注入註解@Autowired

  • 為什麼@Autowired能夠自動注入值,是通過這個AutowiredAnnotationBeanPostProcessor實現BeanPostProcessors介面
  • 在物件建立完之後,處理標註@Autowired標註的所有屬性進行注入值

相關文章