最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Bean的自動注入及循環(huán)依賴問題

 更新時間:2023年03月23日 14:35:20   作者:先熬半個月  
本文詳細(xì)介紹了Bean的自動注入及循環(huán)依賴,文中通過代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)有一定的研究價值,感興趣的小伙伴可以閱讀參考

一、spring中的各種注入方式

對于是spring的注入前置知識、@Autowired、@Resource等的知識可以看其他文章,這里就不多說了

我們對Bean的注入,一般有下面幾種方式:

1)、通過@Autowired、@Resource作用在屬性上

2)、通過@Autowired、@Resource作用在方法上

3)、通過提供set方法+改變注入模型為自動注入

4)、通過BeanDefinition方式完成屬性注入

我們先說前三種方式:

我們用下面的測試類來檢驗:

1、測試注解作用在屬性上:

@Component
public class AnnotationAutowiredFiledBeanTest {
}

2、測試注解作用在方法上:

@Component
public class AnnotationAutowiredMethodBeanTest {
}

3、測試通過提供set方法+改變注入模型為自動注入

@Component
public class AutowiredInjectByTypeMethodBeanTest {
}

修改為注入模型類:

/**
 * 用來設(shè)置SpringBeanInfoTest類的屬性注入為自動注入模式
 *
 * */
@Component
public class UpdateBeanInfoBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		AbstractBeanDefinition a = (AbstractBeanDefinition) beanFactory.getBeanDefinition("a");
		a.setAutowireMode(2);
		//a.getPropertyValues().add("beanDefinitionPropertyValuesBeanTest",new BeanDefinitionPropertyValuesBeanTest());
	}
}

4、各種方式的注入類:

@Component("a")
public class SpringBeanInfoTest {
 
	//@Autowired作用在屬性上進(jìn)行注入
	@Autowired
	AnnotationAutowiredFiledBeanTest autowiredFiledBeanTest;
 
	//@Autowired作用在方法上進(jìn)行注入
	@Autowired
	public void setAnnotationAutowiredMethodBeanTest(AnnotationAutowiredMethodBeanTest annotationAutowiredMethodBeanTest){
		System.out.println("AnnotationAutowiredMethodBeanTest=[{}]"+annotationAutowiredMethodBeanTest);
	}
 
	//使用自動注入(使用byType的模式)
	public void setAutowiredInjectByTypeMethodBeanTest(AutowiredInjectByTypeMethodBeanTest autowiredInjectByTypeMethodBeanTest){
		System.out.println("AutowiredInjectByTypeMethodBeanTest=[{}]"+autowiredInjectByTypeMethodBeanTest);
	}
 
 
 
 
	//用來打印@Autowired作用在屬性上進(jìn)行注入是否成功
	public void printf(){
		System.out.println("autowiredFiledBeanTest=[{}]"+autowiredFiledBeanTest);
	}
 
}

5、啟動測試類:

配置類:

@Configuration
@ComponentScan("com.spring.demo.introspect")
public class App {
 
}

啟動類:

public class ApplicationTest {
 
 
@Test
	public void testSpringInject() throws NoSuchFieldException, IntrospectionException {
		AnnotationConfigApplicationContext context
				= new AnnotationConfigApplicationContext();
		context.register(App.class);
		context.refresh();
		context.getBean(SpringBeanInfoTest.class).printf();
    }
}

此時我們在UpdateBeanInfoBeanFactoryPostProcessor類中設(shè)置了SpringBeanInfoTest類的屬性注入為自動注入(2,默認(rèn)為0)。我們運行下看看注入情況:

此時發(fā)現(xiàn)三種情況都能進(jìn)行注入

此時我們修改注入模型為默認(rèn)的手動注入。

/**
 * 用來設(shè)置SpringBeanInfoTest類的屬性注入為自動注入模式
 *
 * */
@Component
public class UpdateBeanInfoBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		AbstractBeanDefinition a = (AbstractBeanDefinition) beanFactory.getBeanDefinition("a");
		//a.setAutowireMode(2);
		//a.getPropertyValues().add("beanDefinitionPropertyValuesBeanTest",new BeanDefinitionPropertyValuesBeanTest());
	}
}

結(jié)果打印

此時發(fā)現(xiàn)第三種方式無法注入了。

那么此時我們再來測試下第四種方式(通過BeanDefinition方式)

我們知道java內(nèi)省機制最后都是講解析出來的屬性等轉(zhuǎn)換為一個BeanInfo對象,然后所有屬性等信息存在一個PropertyDescriptor數(shù)組中,而spring中在BeanDefinition中定義了一個MutablePropertyValues對象(propertyValues)中的一個List用來定義描述這個類的屬性等,那么我們要往SpringBeanInfoTest中注入一個屬性,此時就可以往這個List中存進(jìn)我們要注入的對象即可。

(其實四種方式都是往propertyValues的List中添加屬性內(nèi)容,最后會對這個list進(jìn)行統(tǒng)一的處理。只是前三種通過不同方式獲取到屬性內(nèi)容,然后放進(jìn)list,而第四種則是直接add進(jìn)行)

我們先編寫一個測試類(這里不用@Component):

public class BeanDefinitionPropertyValuesBeanTest {
}

此時在UpdateBeanInfoBeanFactoryPostProcessor 類中進(jìn)行BeanDefinition方式的注入:

/**
 * 用來設(shè)置SpringBeanInfoTest類的屬性注入為自動注入模式
 *
 * */
@Component
public class UpdateBeanInfoBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		AbstractBeanDefinition a = (AbstractBeanDefinition) beanFactory.getBeanDefinition("a");
		
		a.getPropertyValues().add("beanDefinitionPropertyValuesBeanTest",new BeanDefinitionPropertyValuesBeanTest());
	}
}

這里由于是手動的修改BeanDefinition對象,所以其注入模型并不會影響到這個是否生效。

然后在SpringBeanInfoTest中添加注入方法:

@Component("a")
public class SpringBeanInfoTest {
 
	//@Autowired作用在屬性上進(jìn)行注入
	@Autowired
	AnnotationAutowiredFiledBeanTest autowiredFiledBeanTest;
 
	//@Autowired作用在方法上進(jìn)行注入
	@Autowired
	public void setAnnotationAutowiredMethodBeanTest(AnnotationAutowiredMethodBeanTest annotationAutowiredMethodBeanTest){
		System.out.println("AnnotationAutowiredMethodBeanTest=[{}]"+annotationAutowiredMethodBeanTest);
	}
 
	//使用自動注入(使用byType的模式)
	public void setAutowiredInjectByTypeMethodBeanTest(AutowiredInjectByTypeMethodBeanTest autowiredInjectByTypeMethodBeanTest){
		System.out.println("AutowiredInjectByTypeMethodBeanTest=[{}]"+autowiredInjectByTypeMethodBeanTest);
	}
 
	//添加使用BeanDefinition方式進(jìn)行屬性注入
	public void setBeanDefinitionPropertyValuesBeanTest(BeanDefinitionPropertyValuesBeanTest beanDefinitionPropertyValuesBeanTest){
		System.out.println("BeanDefinitionPropertyValuesBeanTest=[{}]"+beanDefinitionPropertyValuesBeanTest);
	}
	
 
	//用來打印@Autowired作用在屬性上進(jìn)行注入是否成功
	public void printf(){
		System.out.println("autowiredFiledBeanTest=[{}]"+autowiredFiledBeanTest);
	}
 
}

我們可以來看看是否生效:

可以看是可以進(jìn)行注入的。

下面我們可以先簡單的對這四種情況做個總結(jié),后續(xù)再進(jìn)行源碼分析驗證猜想:

1)、在一個屬性上面加一個@Autowired注解

使用反射機制進(jìn)行注入,可以看@Autowired源碼,偽代碼大概如下:

Class clazz = null;
Field autowiredFiledBeanTest = clazz.getDeclaredField("autowiredFiledBeanTest");
autowiredFiledBeanTest.setAccessible(true);
autowiredFiledBeanTest.set(this,getBean(AnnotationAutowiredFiledBeanTest.class));

2)、在一個方法上面加一個@Autowired注解

 2.1如果注入模型是1、2 (自動注入),那么spring底層采用的是java的自省機制發(fā)現(xiàn)setter方法然后調(diào)用執(zhí)行
* 也就是說方法上面的@Autowierd注解無用,直接走內(nèi)省機制進(jìn)行注入而不是通過解析@Autowierd進(jìn)行注入

 2.2如果注入模型為0(默認(rèn)值,手動注入) 那么則是和在屬性上面加注解差不多,底層查找所有加了@Autowired注解的方法,然后反射調(diào)用method.invoke()
3)、提供一個setter方法,繼而把該bean的注入模型改成1、2 自動注入

* 3.1  注入模型是自動注入 則是java的內(nèi)省機制
偽代碼如下:

BeanInfo beanInfo = Introspector.getBeanInfo(SpringBeanInfoTest.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
 
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
	Method writeMethod = propertyDescriptor.getWriteMethod();
	writeMethod.invoke(this,getBean(parma))
}

4)、使用BeanDefinition方式進(jìn)行注入

不和注入模型有相關(guān)聯(lián),即所有情況都能生效

------------------------------------------源碼驗證

入口:refresh---》

finishBeanFactoryInitialization----》beanFactory.preInstantiateSingletons();---》
getBean---》doGetBean---》createBean-----》doCreateBean----》populateBean
我們進(jìn)入populateBean方法看看:

//先從容器中獲取bean,有則直接返回進(jìn)行注入
	//無則調(diào)用createBean創(chuàng)建需要進(jìn)行注入的bean,放進(jìn)單例池,最后再進(jìn)行注入
	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}
 
		// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						return;
					}
				}
			}
		}
 
		//1、獲取到MutablePropertyValues對象,里面的List<PropertyValue> propertyValueList封裝著一些屬性的定義
		//這里現(xiàn)在只能獲取到手動使用BeanDefinition動態(tài)添加的屬性
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
 
		//獲取注入模型
		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		//2、注入模型為1或者2(自動注入),通過內(nèi)省機制獲取所有符合的屬性(包括獲取到使用了@Autowired注解的set),并getbean放進(jìn)propertyValueList中
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			//獲取到符合規(guī)則的屬性(setter)
			//然后獲取到該屬性的bean,并加入到MutablePropertyValues中的List中
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			//獲取到符合規(guī)則的屬性(setter)
			// 然后獲取到該屬性的bean,并加入到MutablePropertyValues中的List中
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			//所有符合上面的屬性都會加到這里
			pvs = newPvs;
		}
 
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
 
		//3、如果注入模型為0,手動注入,此時這里的propertyValueList只會存在我們手動使用BeanDefinition add進(jìn)去的。
		//那么到這里為止所有set方法都沒被識別到(既不會在applyPropertyValues中執(zhí)行了)
		//下面的循環(huán)則是去解析@Autowired作用的屬性、方法(反射機制)
		//注意:如果該屬性已經(jīng)存在propertyValueList,這里則不會對其進(jìn)行解析(即自動注入模型下@Autowired作用在方法的被忽略執(zhí)行)
		PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
 
			//獲取到所有BeanPostProcessor
			//如果是InstantiationAwareBeanPostProcessor,即處理@Autowired注解、@Resouce注解、@PostConstruct注解的BeanPostProcessor類型,則完成注入等操作
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				//完成@Autowired作用在屬性、方法上面的處理(使用反射)
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					//完成@Autowired作用在屬性、方法上面的處理
					PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						if (filteredPds == null) {
							filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
						}
						pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvsToUse == null) {
							return;
						}
					}
					pvs = pvsToUse;
				}
			}
		}
		if (needsDepCheck) {
			if (filteredPds == null) {
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}
 
		//4、處理propertyValueList中的所有還未執(zhí)行的屬性
		//遍歷屬性名、對象,內(nèi)省機制調(diào)用invoke方法執(zhí)行set方法等
		if (pvs != null) {
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

我們總結(jié)下:

1、先獲取手動添加的propertyValueList的(通過BeanDefinition進(jìn)行add的)

2、如果注入模型為自動注入,則通過內(nèi)省機制獲取所有符合的規(guī)則的屬性(包含使用注解、未使用注解的),getBean獲取到bean對象,并加入到propertyValueList中

3、通過反射獲取使用@Autowired注解的屬性,并解析執(zhí)行。這里有幾點需要注意

1)、解析包含@Autowired作用的屬性和方法兩種

2)、如果該屬性存在propertyValueList則不進(jìn)行解析(即當(dāng)注入模型為自動注入時,@Autowired作用的方法上的方式在步驟2中使用內(nèi)省機制進(jìn)行獲取,此時跳過第三步)

4、對propertyValueList的所有屬性調(diào)用invoke方法執(zhí)行

不同方式在不同注入模型下獲取屬性的方式不同(內(nèi)省機制、反射機制)

到此這篇關(guān)于Bean的自動注入及循環(huán)依賴問題的文章就介紹到這了,更多相關(guān)Bean的自動注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解springboot之jackson的兩種配置方式

    詳解springboot之jackson的兩種配置方式

    這篇文章主要介紹了詳解springboot之jackson的兩種配置方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • Java Spring Cloud 負(fù)載均衡詳解

    Java Spring Cloud 負(fù)載均衡詳解

    這篇文章主要介紹了Spring Cloud負(fù)載均衡及遠(yuǎn)程調(diào)用實現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2021-09-09
  • 詳解Java 連接MongoDB集群的幾種方式

    詳解Java 連接MongoDB集群的幾種方式

    這篇文章主要介紹了詳解Java 連接MongoDB集群的幾種方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • SpringBoot詳解整合Spring?Cache實現(xiàn)Redis緩存流程

    SpringBoot詳解整合Spring?Cache實現(xiàn)Redis緩存流程

    這篇文章主要介紹了SpringBoot整合Spring?Cache實現(xiàn)Redis緩存方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • java動態(tài)目錄樹的實現(xiàn)示例

    java動態(tài)目錄樹的實現(xiàn)示例

    在開發(fā)過程中,常常需要對目錄結(jié)構(gòu)進(jìn)行操作和展示,本文主要介紹了java動態(tài)目錄樹的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • SpringBoot如何獲取當(dāng)前操作用戶的id/信息

    SpringBoot如何獲取當(dāng)前操作用戶的id/信息

    在一般性的基設(shè)需求中,有需要獲取當(dāng)前用戶操作記錄的情況,也就是說我們需要記錄當(dāng)前用戶的信息,如:id、昵稱、賬號等信息,這篇文章主要介紹了SpringBoot獲取當(dāng)前操作用戶的id/信息,需要的朋友可以參考下
    2023-10-10
  • Spring?Boot實現(xiàn)MyBatis動態(tài)創(chuàng)建表的操作語句

    Spring?Boot實現(xiàn)MyBatis動態(tài)創(chuàng)建表的操作語句

    這篇文章主要介紹了Spring?Boot實現(xiàn)MyBatis動態(tài)創(chuàng)建表,MyBatis提供了動態(tài)SQL,我們可以通過動態(tài)SQL,傳入表名等信息然組裝成建表和操作語句,本文通過案例講解展示我們的設(shè)計思路,需要的朋友可以參考下
    2024-01-01
  • SpringCloud使用Feign實現(xiàn)遠(yuǎn)程調(diào)用流程詳細(xì)介紹

    SpringCloud使用Feign實現(xiàn)遠(yuǎn)程調(diào)用流程詳細(xì)介紹

    OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成
    2023-02-02
  • 微信公眾號開發(fā)之設(shè)置自定義菜單實例代碼【java版】

    微信公眾號開發(fā)之設(shè)置自定義菜單實例代碼【java版】

    這篇文章主要介紹了微信公眾號開發(fā)之設(shè)置自定義菜單實例代碼,本實例是為了實現(xiàn)在管理后臺實現(xiàn)微信菜單的添加刪除管理。需要的朋友可以參考下
    2018-06-06
  • MyBatis高級映射ResultMap解決屬性問題

    MyBatis高級映射ResultMap解決屬性問題

    對于數(shù)據(jù)庫中對表的增刪改查操作,我們知道增刪改都涉及的是單表,而只有查詢操作既可以設(shè)計到單表操作又可以涉及到多表操作,所以對于輸入映射parameterType而言是沒有所謂的高級映射的,也就是說高級映射只針對于輸出映射
    2023-02-02

最新評論

七台河市| 普洱| 张家界市| 新绛县| 余姚市| 娄烦县| 双牌县| 通州区| 盐津县| 敦化市| 昭通市| 迭部县| 平陆县| 扎赉特旗| 乐平市| 桑植县| 广饶县| 米林县| 龙陵县| 兴宁市| 晋江市| 大竹县| 博野县| 崇州市| 泰宁县| 兴和县| 通州区| 息烽县| 永丰县| 天台县| 玛曲县| 青岛市| 元朗区| 宜宾市| 牡丹江市| 郴州市| 靖远县| 乌拉特前旗| 长泰县| 新源县| 扬州市|