<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
對於是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 { }
修改為注入模型類:
/** * 用來設定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作用在屬性上進行注入 @Autowired AnnotationAutowiredFiledBeanTest autowiredFiledBeanTest; //@Autowired作用在方法上進行注入 @Autowired public void setAnnotationAutowiredMethodBeanTest(AnnotationAutowiredMethodBeanTest annotationAutowiredMethodBeanTest){ System.out.println("AnnotationAutowiredMethodBeanTest=[{}]"+annotationAutowiredMethodBeanTest); } //使用自動注入(使用byType的模式) public void setAutowiredInjectByTypeMethodBeanTest(AutowiredInjectByTypeMethodBeanTest autowiredInjectByTypeMethodBeanTest){ System.out.println("AutowiredInjectByTypeMethodBeanTest=[{}]"+autowiredInjectByTypeMethodBeanTest); } //用來列印@Autowired作用在屬性上進行注入是否成功 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類中設定了SpringBeanInfoTest類的屬性注入為自動注入(2,預設為0)。我們執行下看看注入情況:
此時發現三種情況都能進行注入
此時我們修改注入模型為預設的手動注入。
/** * 用來設定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()); } }
結果列印
此時發現第三種方式無法注入了。
那麼此時我們再來測試下第四種方式(通過BeanDefinition方式)
我們知道java內省機制最後都是講解析出來的屬性等轉換為一個BeanInfo物件,然後所有屬性等資訊存在一個PropertyDescriptor陣列中,而spring中在BeanDefinition中定義了一個MutablePropertyValues物件(propertyValues)中的一個List用來定義描述這個類的屬性等,那麼我們要往SpringBeanInfoTest中注入一個屬性,此時就可以往這個List中存進我們要注入的物件即可。
(其實四種方式都是往propertyValues的List中新增屬性內容,最後會對這個list進行統一的處理。只是前三種通過不同方式獲取到屬性內容,然後放進list,而第四種則是直接add進行)
我們先編寫一個測試類(這裡不用@Component):
public class BeanDefinitionPropertyValuesBeanTest { }
此時在UpdateBeanInfoBeanFactoryPostProcessor 類中進行BeanDefinition方式的注入:
/** * 用來設定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作用在屬性上進行注入 @Autowired AnnotationAutowiredFiledBeanTest autowiredFiledBeanTest; //@Autowired作用在方法上進行注入 @Autowired public void setAnnotationAutowiredMethodBeanTest(AnnotationAutowiredMethodBeanTest annotationAutowiredMethodBeanTest){ System.out.println("AnnotationAutowiredMethodBeanTest=[{}]"+annotationAutowiredMethodBeanTest); } //使用自動注入(使用byType的模式) public void setAutowiredInjectByTypeMethodBeanTest(AutowiredInjectByTypeMethodBeanTest autowiredInjectByTypeMethodBeanTest){ System.out.println("AutowiredInjectByTypeMethodBeanTest=[{}]"+autowiredInjectByTypeMethodBeanTest); } //新增使用BeanDefinition方式進行屬性注入 public void setBeanDefinitionPropertyValuesBeanTest(BeanDefinitionPropertyValuesBeanTest beanDefinitionPropertyValuesBeanTest){ System.out.println("BeanDefinitionPropertyValuesBeanTest=[{}]"+beanDefinitionPropertyValuesBeanTest); } //用來列印@Autowired作用在屬性上進行注入是否成功 public void printf(){ System.out.println("autowiredFiledBeanTest=[{}]"+autowiredFiledBeanTest); } }
我們可以來看看是否生效:
可以看是可以進行注入的。
下面我們可以先簡單的對這四種情況做個總結,後續再進行原始碼分析驗證猜想:
1)、在一個屬性上面加一個@Autowired註解
使用反射機制進行注入,可以看@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的自省機制發現setter方法然後呼叫執行
* 也就是說方法上面的@Autowierd註解無用,直接走內省機制進行注入而不是通過解析@Autowierd進行注入
2.2如果注入模型為0(預設值,手動注入) 那麼則是和在屬性上面加註解差不多,底層查詢所有加了@Autowired註解的方法,然後反射呼叫method.invoke()
3)、提供一個setter方法,繼而把該bean的注入模型改成1、2 自動注入
* 3.1 注入模型是自動注入 則是java的內省機制
虛擬碼如下:
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方式進行注入
不和注入模型有相關聯,即所有情況都能生效
------------------------------------------原始碼驗證
入口:refresh---》
finishBeanFactoryInitialization----》beanFactory.preInstantiateSingletons();---》
getBean---》doGetBean---》createBean-----》doCreateBean----》populateBean
我們進入populateBean方法看看:
//先從容器中獲取bean,有則直接返回進行注入 //無則呼叫createBean建立需要進行注入的bean,放進單例池,最後再進行注入 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封裝著一些屬性的定義 //這裡現在只能獲取到手動使用BeanDefinition動態新增的屬性 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); //獲取注入模型 int resolvedAutowireMode = mbd.getResolvedAutowireMode(); //2、注入模型為1或者2(自動注入),通過內省機制獲取所有符合的屬性(包括獲取到使用了@Autowired註解的set),並getbean放進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. //獲取到符合規則的屬性(setter) //然後獲取到該屬性的bean,並加入到MutablePropertyValues中的List中 if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. //獲取到符合規則的屬性(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進去的。 //那麼到這裡為止所有set方法都沒被識別到(既不會在applyPropertyValues中執行了) //下面的迴圈則是去解析@Autowired作用的屬性、方法(反射機制) //注意:如果該屬性已經存在propertyValueList,這裡則不會對其進行解析(即自動注入模型下@Autowired作用在方法的被忽略執行) 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中的所有還未執行的屬性 //遍歷屬性名、物件,內省機制呼叫invoke方法執行set方法等 if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); } }
我們總結下:
1、先獲取手動新增的propertyValueList的(通過BeanDefinition進行add的)
2、如果注入模型為自動注入,則通過內省機制獲取所有符合的規則的屬性(包含使用註解、未使用註解的),getBean獲取到bean物件,並加入到propertyValueList中
3、通過反射獲取使用@Autowired註解的屬性,並解析執行。這裡有幾點需要注意
1)、解析包含@Autowired作用的屬性和方法兩種
2)、如果該屬性存在propertyValueList則不進行解析(即當注入模型為自動注入時,@Autowired作用的方法上的方式在步驟2中使用內省機制進行獲取,此時跳過第三步)
4、對propertyValueList的所有屬性呼叫invoke方法執行
不同方式在不同注入模型下獲取屬性的方式不同(內省機制、反射機制)
到此這篇關於Bean的自動注入及迴圈依賴問題的文章就介紹到這了,更多相關Bean的自動注入內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45