欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring注解驅(qū)動之BeanPostProcessor后置處理器講解

 更新時間:2022年09月30日 10:01:00   作者:融極  
這篇文章主要介紹了Spring注解驅(qū)動之BeanPostProcessor后置處理器講解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

概述

在學(xué)習(xí)Spring的時候,在了解基本用法的時候,如果有時間一定要深入源碼了解Spring的底層原理,這樣在做一些適配工作、寫一些輪子的時候就會比較容易,否則會很難,甚至一頭霧水,無法完成工作。

吃透Spring的原理和源碼,往往可以拉開人們之間的差距,當(dāng)前只要是使用Java技術(shù)棧開發(fā)的Web項(xiàng)目,幾乎都會使用Spring框架。

而且目前各招聘網(wǎng)站上對于Java開發(fā)的要求幾乎清一色的都是熟悉或者精通Spring,所以,你很有必要學(xué)習(xí)Spring的細(xì)節(jié)知識點(diǎn)。

BeanPostProcessor后置處理器概述

首先,看下BeanPostProcessor的源碼。

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;

public interface BeanPostProcessor {
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

從源碼可以看出,BeanPostProcessor是一個接口,其中有兩個方法:

postProcessBeforeInitialization和postProcessAfterInitialization這兩個方法分別是在Spring容器中的bean初始化前后執(zhí)行,所以Spring容器中的每個bean對象初始化前后,都會執(zhí)行BeanPostProcessor接口的兩個方法。

也就是說,postProcessBeforeInitialization方法會在bean實(shí)例化和屬性設(shè)置之后,自定義初始化方法之前被調(diào)用,而postProcessAfterInitialization方法會在自定義初始化方法之后被調(diào)用。當(dāng)容器中存在多個BeanPostProcessor的實(shí)現(xiàn)類時,會按照它們在容器中注冊的順序執(zhí)行。對于自定義的BeanPostProcessor實(shí)現(xiàn)類,還可以讓其實(shí)現(xiàn)Ordered接口自定義排序。

因此我們可以在每個bean對象初始化前后,加上自己的邏輯。實(shí)現(xiàn)方式是自定義一個BeanPostProcessor接口的實(shí)現(xiàn)類,例如MyBeanPostProcessor,然后在該類的postProcessBeforeInitialization和postProcessAfterInitialization這兩個方法寫上自定義邏輯。

BeanPostProcessor后置處理器實(shí)例

我們創(chuàng)建一個MyBeanPostProcessor類,實(shí)現(xiàn)BeanPostProcessor接口,如下所示。

package com.meimeixia.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component // 將后置處理器加入到容器中,這樣的話,Spring就能讓它工作了,否則無法工作
public class MyBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
		return bean;
	}
}

接下來,我們應(yīng)該是要編寫測試用例來進(jìn)行測試了。

package com.meimeixia.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import com.meimeixia.bean.Car;

@ComponentScan("com.meimeixia.bean")
@Configuration
public class MainConfigOfLifeCycle {

	@Bean(initMethod="init", destroyMethod="destroy")
	public Car car() {
		return new Car();
	}	
}

第二處改動是將Cat類上添加的@Scope(“prototype”)注解給注釋掉,因?yàn)樵蹅冎白鰷y試的時候,也是將Cat對象設(shè)置成多實(shí)例bean了。

package com.meimeixia.bean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

// @Scope("prototype")
@Component
public class Cat implements InitializingBean, DisposableBean {
	
	public Cat() {
		System.out.println("cat constructor...");
	}

	/**
	 * 會在容器關(guān)閉的時候進(jìn)行調(diào)用
	 */
	@Override
	public void destroy() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("cat destroy...");
	}

	/**
	 * 會在bean創(chuàng)建完成,并且屬性都賦好值以后進(jìn)行調(diào)用
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("cat afterPropertiesSet...");
	}
}

好了,現(xiàn)在咱們就可以編寫測試用例來進(jìn)行測試了。

可喜的是,我們也不用再編寫一個測試用例了,直接運(yùn)行IOCTest_LifeCycle類中的test01()方法就行,該方法的代碼如下所示。

@Test
public void test01() {
    // 1. 創(chuàng)建IOC容器
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("容器創(chuàng)建完成");
    // 關(guān)閉容器
    applicationContext.close();
}

此時,運(yùn)行IOCTest_LifeCycle類中的test01()方法,輸出的結(jié)果信息如下所示。

可以看到,postProcessBeforeInitialization方法會在bean實(shí)例化和屬性設(shè)置之后,自定義初始化方法之前被調(diào)用,而postProcessAfterInitialization方法會在自定義初始化方法之后被調(diào)用。

當(dāng)然了,也可以讓我們自己寫的MyBeanPostProcessor類來實(shí)現(xiàn)Ordered接口自定義排序,如下所示。

package com.meimeixia.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * 后置處理器,在初始化前后進(jìn)行處理工作
 * @author liayun
 *
 */
@Component // 將后置處理器加入到容器中,這樣的話,Spring就能讓它工作了
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
		return bean;
	}

	@Override
	public int getOrder() {
		return 3;
	}
}

BeanPostProcessor后置處理器作用

后置處理器可用于bean對象初始化前后進(jìn)行邏輯增強(qiáng)。

Spring提供了BeanPostProcessor接口的很多實(shí)現(xiàn)類,例如AutowiredAnnotationBeanPostProcessor用于@Autowired注解的實(shí)現(xiàn),AnnotationAwareAspectJAutoProxyCreator用于Spring AOP的動態(tài)代理等等。

除此之外,我們還可以自定義BeanPostProcessor接口的實(shí)現(xiàn)類,在其中寫入咱們需要的邏輯。

bean的初始化和銷毀流程

我們知道BeanPostProcessor的postProcessBeforeInitialization()方法是在bean的初始化之前被調(diào)用;而postProcessAfterInitialization()方法是在bean初始化的之后被調(diào)用。

并且bean的初始化和銷毀方法我們可以通過如下方式進(jìn)行指定。

1. 通過@Bean指定init-method和destroy-method

@Bean(initMethod="init", destroyMethod="destroy")

2. 通過讓bean實(shí)現(xiàn)InitializingBean和DisposableBean這倆接口

@Componentpublic class Cat implements InitializingBean, DisposableBean {}

3. 使用JSR-250規(guī)范里面定義的@PostConstruct和@PreDestroy這倆注解

  • @PostConstruct:在bean創(chuàng)建完成并且屬性賦值完成之后,來執(zhí)行初始化方法
  • @PreDestroy:在容器銷毀bean之前通知我們進(jìn)行清理工作

4. 通過讓bean實(shí)現(xiàn)BeanPostProcessor接口

@Component // 將后置處理器加入到容器中,這樣的話,Spring就能讓它工作了
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {}

通過以上四種方式就可以對bean的整個生命周期進(jìn)行控制:

  • bean的實(shí)例化:調(diào)用bean的構(gòu)造方法,我們可以在bean的無參構(gòu)造方法中執(zhí)行相應(yīng)的邏輯。
  • bean的初始化:在初始化時可以通過BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法進(jìn)行攔截,執(zhí)行自定義的邏輯。通過@PostConstruct注解、InitializingBean和init-method來指定bean初始化前后執(zhí)行的方法,在該方法中可以執(zhí)行自定義的邏輯。
  • bean的銷毀:可以通過@PreDestroy注解、DisposableBean和destroy-method來指定bean在銷毀前執(zhí)行的方法,在方法中可以執(zhí)行自定義的邏輯。

所以,通過上述4種方式,我們可以控制Spring中bean的整個生命周期。

BeanPostProcessor源碼解析

如果想深刻理解BeanPostProcessor的工作原理,那么就不得不看下相關(guān)的源碼,我們可以在MyBeanPostProcessor類的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法這兩處打上斷點(diǎn)來進(jìn)行調(diào)試,如下所示。 

public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization" + ",beanName:"+beanName + ",bean=>" + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization" + ",beanName:"+beanName + ",bean=>" + bean);
        return bean;
    }

    @Override
    public int getOrder() {
        return 3;
    }
}

通過斷點(diǎn)調(diào)試,我們可以看到,在applyBeanPostProcessorBeforeInitialization()方法中,會遍歷所有BeanPostProcessor對象,然后依次執(zhí)行所有BeanPostProcessor對象的postProcessorBeforeInitialization()方法,一旦BeanPostProcessor對象的postProcessBeforeInitialization()方法返回null以后,則后面的BeanPostProcessor對象便不再執(zhí)行了,而是直接退出for循環(huán)。這些都是我們看源碼看到的。

看Spring源碼,我們還看到一個細(xì)節(jié),在Spring中調(diào)用initializeBean()方法之前,還調(diào)用了populateBean()方法來為bean的屬性賦值。

經(jīng)過一系列的跟蹤源碼分析,我們可以將關(guān)鍵代碼的調(diào)用過程使用如下偽代碼表述出來。

populateBean(beanName, mbd, instanceWrapper); // 給bean進(jìn)行屬性賦值
initializeBean(beanName, exposedObject, mbd)
{
	applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	invokeInitMethods(beanName, wrappedBean, mbd); // 執(zhí)行自定義初始化
	applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

也就是說,在Spring中,調(diào)用initializeBean()方法之前,調(diào)用了populateBean()方法為bean的屬性賦值,為bean的屬性賦好值之后,再調(diào)用initializeBean()方法進(jìn)行初始化。

在initializeBean()中,調(diào)用自定義的初始化方法(即invokeInitMethods())之前,調(diào)用了applyBeanPostProcessorsBeforeInitialization()方法,而在調(diào)用自定義的初始化方法之后,又調(diào)用了applyBeanPostProcessorsAfterInitialization()方法。至此,整個bean的初始化過程就這樣結(jié)束了。

BeanPostProcessor接口在Spring底層的應(yīng)用案例

ApplicationContextAwareProcessor類

org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的一個實(shí)現(xiàn)類,這個類的作用是可以向組件中注入IOC容器,大致的源碼如下。

注意:我這里的Spring版本為4.3.12.RELEASE。

那具體如何使用ApplicationContextAwareProcessor類向組件中注入IOC容器呢?

如果需要向組件中注入IOC容器,那么可以讓組件實(shí)現(xiàn)ApplicationContextAware接口。

例如,我們創(chuàng)建一個創(chuàng)建一個Dog類,使其實(shí)現(xiàn)ApplicationContextAware接口,此時,我們需要實(shí)現(xiàn)ApplicationContextAware接口中的setApplicationContext()方法,在setApplicationContext()方法中有一個ApplicationContext類型的參數(shù),這個就是IOC容器對象,我們可以在Dog類中定義一個ApplicationContext類型的成員變量,然后在setApplicationContext()方法中為這個成員變量賦值,此時就可以在Dog類中的

其他方法中使用ApplicationContext對象了,如下所示。

package com.meimeixia.bean;

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

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

/**
 * ApplicationContextAwareProcessor這個類的作用是可以幫我們在組件里面注入IOC容器,
 * 怎么注入呢?我們想要IOC容器的話,比如我們這個Dog組件,只需要實(shí)現(xiàn)ApplicationContextAware接口就行
 * 
 */
@Component
public class Dog implements ApplicationContextAware {
	
	private ApplicationContext applicationContext;

	public Dog() {
		System.out.println("dog constructor...");
	}
	
	// 在對象創(chuàng)建完成并且屬性賦值完成之后調(diào)用
	@PostConstruct
	public void init() { 
		System.out.println("dog...@PostConstruct...");
	}
	
	// 在容器銷毀(移除)對象之前調(diào)用
	@PreDestroy
	public void destory() {
		System.out.println("dog...@PreDestroy...");
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 在這兒打個斷點(diǎn)調(diào)試一下
		// TODO Auto-generated method stub
		this.applicationContext = applicationContext;
	}	
}

看到這里,相信不少小伙伴們都有一種很熟悉的感覺,沒錯,我之前也在項(xiàng)目中使用過!是的,這就是BeanPostProcessor在Spring底層的一種使用場景。至于上面的案例代碼為何會在setApplicationContext()方法中獲取到ApplicationContext對象,這就是ApplicationContextAwareProcessor類的功勞了!

接下來,我們就深入分析下ApplicationContextAwareProcessor類。

我們先來看下ApplicationContextAwareProcessor類中對于postProcessBeforeInitialization()方法的實(shí)現(xiàn),如下所示。

在bean初始化之前,首先對當(dāng)前bean的類型進(jìn)行判斷,如果當(dāng)前bean的類型不是EnvironmentAware,不是EmbeddedValueResolverAware,不是ResourceLoaderAware,不是ApplicationEventPublisherAware,不是MessageSourceAware,也不是ApplicationContextAware,那么直接返回bean。

如果是上面類型中的一種類型,那么最終會調(diào)用invokeAwareInterfaces()方法,并將bean傳遞給該方法。

invokeAwareInterfaces()方法又是個什么呢?我們繼續(xù)看invokeAwareInterfaces()方法的源碼,如下所示。

可以看到invokeAwareInterfaces()方法的源碼比較簡單,就是判斷當(dāng)前bean屬于哪種接口類型,然后將bean強(qiáng)轉(zhuǎn)為哪種接口類型的對象,接著調(diào)用接口中的方法,將相應(yīng)的參數(shù)傳遞到接口的方法中。

我們可以看到,此時會將this.applicationContext傳遞到ApplicationContextAware接口的setApplicationContext()方法中。所以,我們在Dog類的setApplicationContext()方法中就可以直接接收到ApplicationContext對象了。

BeanValidationPostProcessor類

org.springframework.validation.beanvalidation.BeanValidationPostProcessor類注意是用來為bean進(jìn)行校驗(yàn)操作的,當(dāng)我們創(chuàng)建bean,并為bean賦值后,我們可以通過BeanValidationPostProcessor類為bean進(jìn)行校驗(yàn)操作。BeanValidationPostProcessor類源碼如下:

這里,我們也來看看postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法的實(shí)現(xiàn),如下所示。

InitDestroyAnnotationBeanPostProcessor類

org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor類主要用來處理@PostConstruct注解和@PreDestroy注解。

使用了@PostConstruct注解和@PreDestroy注解來標(biāo)注方法,Spring怎么就知道什么時候執(zhí)行@PostConstruct注解標(biāo)注的方法,什么時候執(zhí)行@PreDestroy注解標(biāo)注的方法呢?這就要?dú)w功于InitDestroyAnnotationBeanPostProcessor類了。

接下來,我們也通過Debug的方式來跟進(jìn)下代碼的執(zhí)行流程。首先,在Dog類的initt()方法上打上一個斷點(diǎn),如下所示。

在InitDestroyAnnotationBeanPostProcessor類的postProcessBeforeInitialization()方法中,首先會找到bean中有關(guān)生命周期的注解,比如@PostConstruct注解等,找到這些注解之后,則將這些信息賦值給LifecycleMetadata類型的變量metadata,之后調(diào)用metadata的invokeInitMethods()方法,通過反射來調(diào)用標(biāo)注了@PostConstruct注解的方法。

這就是為什么標(biāo)注了@PostConstruct注解的方法會被Spring執(zhí)行的原因。

	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
		try {
			metadata.invokeInitMethods(bean, beanName);
		}
		catch (InvocationTargetException ex) {
			throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
		}
		return bean;
	}

AutowiredAnnotationBeanPostProcessor類

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor類主要是用于處理標(biāo)注了@Autowired注解的變量或方法。

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring Bean生命周期源碼原理圖解

    Spring Bean生命周期源碼原理圖解

    這篇文章主要介紹了Spring Bean生命周期源碼原理圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • Springboot 掃描mapper接口的2種操作

    Springboot 掃描mapper接口的2種操作

    這篇文章主要介紹了Springboot 掃描mapper接口的2種操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • 輕松掌握J(rèn)ava享元模式

    輕松掌握J(rèn)ava享元模式

    這篇文章主要幫助大家輕松掌握J(rèn)ava享元模式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • IntelliJ IDEA打開多個Maven的module且相互調(diào)用代碼的方法

    IntelliJ IDEA打開多個Maven的module且相互調(diào)用代碼的方法

    這篇文章主要介紹了IntelliJ IDEA打開多個Maven的module且相互調(diào)用代碼的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • Java的垃圾強(qiáng)制回收實(shí)例分析

    Java的垃圾強(qiáng)制回收實(shí)例分析

    這篇文章主要介紹了Java的垃圾強(qiáng)制回收,結(jié)合實(shí)例形式分析了java垃圾強(qiáng)制回收的相關(guān)原理及實(shí)現(xiàn)方法,需要的朋友可以參考下
    2019-08-08
  • Java工程使用ffmpeg進(jìn)行音視頻格式轉(zhuǎn)換的實(shí)現(xiàn)

    Java工程使用ffmpeg進(jìn)行音視頻格式轉(zhuǎn)換的實(shí)現(xiàn)

    FFmpeg是一套可以用來記錄、轉(zhuǎn)換數(shù)字音頻、視頻,并能將其轉(zhuǎn)化為流的開源計算機(jī)程序,本文主要介紹了Java工程使用ffmpeg進(jìn)行音視頻格式轉(zhuǎn)換的實(shí)現(xiàn)
    2024-02-02
  • Mybatis省略@Param注解原理分析

    Mybatis省略@Param注解原理分析

    這篇文章主要介紹了Mybatis省略@Param注解原理分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Storm框架整合springboot的方法

    Storm框架整合springboot的方法

    Storm框架中的每個Spout和Bolt都相當(dāng)于獨(dú)立的應(yīng)用,Strom在啟動spout和bolt時提供了一個open方法(spout)和prepare方法(bolt)。這篇文章主要介紹了Storm框架整合springboot的方法,需要的朋友可以參考下
    2018-11-11
  • intelliJ IDEA 多行選中相同內(nèi)容的快捷鍵分享

    intelliJ IDEA 多行選中相同內(nèi)容的快捷鍵分享

    這篇文章主要介紹了intelliJ IDEA 多行選中相同內(nèi)容的快捷鍵分享,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java使用poi操作excel實(shí)例解析

    Java使用poi操作excel實(shí)例解析

    這篇文章主要為大家詳細(xì)介紹了Java使用poi操作excel的簡單實(shí)例,感興趣的小伙伴們可以參考一下
    2016-05-05

最新評論