前言
- 本文將總結(jié)下Spring依賴注入靜態(tài)屬性失敗以及添加set方法就能解決的原理
一川蒙、測(cè)試項(xiàng)目
- AppConfig.java
@Configuration @ComponentScan("com.eugene.sumarry.csdn.autowiredstatic") public class AppConfig { }
- UserDao.java
@Repository public class UserDao { }
- UserService.java
@Service public class UserService { @Autowired private UserDao userDao; @Override public String toString() { return "UserService{" + "userDao=" + userDao + '}'; } }
- Entry.java
public class Entry { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); System.out.println(context.getBean(UserService.class)); } }
二己单、問(wèn)題重現(xiàn)及解決方案
-
UserService.java中依賴注入U(xiǎn)serDao靜態(tài)變量及運(yùn)行結(jié)果(
靜態(tài)變量注入失敗
):
-
UserService.java中依賴注入U(xiǎn)serDao實(shí)例實(shí)例變量及運(yùn)行結(jié)果(實(shí)例變量注入成功):
-
添加set方法完成靜態(tài)變量的依賴注入(
靜態(tài)變量注入成功
):
注意: 使用構(gòu)造方法也可以完成注入纽绍,但是使用構(gòu)造方法來(lái)注入并不是@Autowired注解完成的功能撩荣,因?yàn)槟銜?huì)發(fā)現(xiàn),你加與不加@Autowired注解都會(huì)完成注入肉微。此時(shí)是通過(guò)構(gòu)造方法進(jìn)行依賴注入的(如果讀者不信遵馆,讀完下面的 【原理】章節(jié)后可以自己debug調(diào)試)。本文講解的是@Autowired注解的原理赦肃,所以不考慮構(gòu)造方法的依賴注入
三溅蛉、原理
-
背景知識(shí): Spring的依賴注入方式有很多種,對(duì)Spring而言常見(jiàn)的有如下四種
常見(jiàn)依賴注入類型 備注 AbstractBeanDefinition.AUTOWIRE_NO 不開啟自動(dòng)裝配功能 AbstractBeanDefinition.AUTOWIRE_BY_NAME 根據(jù)變量名來(lái)自動(dòng)裝配 AbstractBeanDefinition.AUTOWIRE_BY_TYPE 根據(jù)類型自動(dòng)裝配 AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR 根據(jù)構(gòu)造方法自動(dòng)裝配 AutowiredAnnotationBeanPostProcessor
后置處理器來(lái)處理船侧,針對(duì)于此篇博客而言,是它的MergedBeanDefinitionPostProcessor身份起的作用
關(guān)于此后置處理器的作用可以查看我之前發(fā)布的博客:spring 5.0.x源碼學(xué)習(xí)系列八: 實(shí)例化bean之使用構(gòu)造方法創(chuàng)建bean厅各、自動(dòng)裝配與循環(huán)依賴的第三章: spring bean實(shí)例化過(guò)程中涉及到的后置處理器和執(zhí)行順序镜撩。最終,經(jīng)過(guò)AutowiredAnnotationBeanPostProcessor
后置處理器的處理队塘,會(huì)將當(dāng)前類的所有支持自動(dòng)裝配屬性以InjectionMetadata
類型的對(duì)象保存袁梗,那到底是支持哪些屬性的自動(dòng)裝配的呢?繼續(xù)往下看憔古。遮怜。
3.1 spring如何選擇@Autowired注解標(biāo)識(shí)的變量進(jìn)行依賴注入
-
請(qǐng)先看下圖中的注釋及源碼解釋
-
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata源碼分析
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { // 存放當(dāng)前類包括父類中帶@Autowired注解的字段和方法 List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); Class<?> targetClass = clazz; do { // 存放targetClass中所有帶了@Autowired注解的字段和方法 final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> { // JDK1.8 新特性,傳入一個(gè)方法進(jìn)去投放,在方法內(nèi)部就是獲取當(dāng)前類的所有字段(不包括父類奈泪, // 包括自己定義的私有變量)适贸,并循環(huán)調(diào)用傳入的方法灸芳, // 即當(dāng)前方法 // 判斷當(dāng)前字段是否有@Autowired注解 AnnotationAttributes ann = findAutowiredAnnotation(field); if (ann != null) { // 判斷當(dāng)前字段是否為static修飾 ===> 靜態(tài)變量, 如果是涝桅,則只是返回了 if (Modifier.isStatic(field.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static fields: " + field); } return; } boolean required = determineRequiredStatus(ann); // 將字段包裝成AutowiredFieldElement對(duì)象,并存入一開始創(chuàng)建的list中 currElements.add(new AutowiredFieldElement(field, required)); } }); ReflectionUtils.doWithLocalMethods(targetClass, method -> { // 同上烙样,此時(shí)獲取的是當(dāng)前class內(nèi)部的method(不包括父類冯遂, 包括自己定義的私有方法 // ),并挨個(gè)遍歷執(zhí)行當(dāng)前傳入的方法 // 判斷遍歷的方法是否為橋接方法 Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } // 拿到當(dāng)前方法的@Autowired注解谒获,并進(jìn)行校驗(yàn)?zāi)玫秸鎸?shí)方法(因?yàn)橛锌赡墚?dāng)前要處理的類是一個(gè)代理對(duì)象蛤肌,或者接口等等) AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { // 判斷當(dāng)前方法是否為靜態(tài)方法 if (Modifier.isStatic(method.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static methods: " + method); } return; } if (method.getParameterCount() == 0) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation should only be used on methods with parameters: " + method); } } boolean required = determineRequiredStatus(ann); // 找到當(dāng)前方法中的參數(shù)描述器 PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); // 將set方法和要傳入的屬性的描述器包裝成AutowiredMethodElement類并添加至一開始創(chuàng)建的list集合中 currElements.add(new AutowiredMethodElement(method, required, pd)); } }); elements.addAll(0, currElements); // 獲取父類的class,針對(duì)父類的屬性和方法在做篩選 targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); // 最終返回一個(gè)InjectionMetadata對(duì)象批狱,其中包含當(dāng)前類及其父類(不包含Object類)的所有帶 // @Autowired注解的方法和子彈 return new InjectionMetadata(clazz, elements); }
綜上裸准,AutowiredAnnotationBeanPostProcessor后置處理器的MergedBeanDefinitionPostProcessors后置處理器的作用就是將當(dāng)前類及其父類(不包含Object類)的所有包含@Autowired注解的非靜態(tài)字段和非靜態(tài)帶參方法以InjectionMetadata對(duì)象的方式保存在
injectionMetadataCache
屬性中
3.2 spring在進(jìn)行依賴注入時(shí)的邏輯
- 在進(jìn)行依賴注入時(shí),肯定是執(zhí)行了
populateBean
方法赔硫,具體結(jié)果如下圖所示:
至此炒俱,spring針對(duì)依賴注入的功能的準(zhǔn)備工作
算是完成了。為什么說(shuō)是準(zhǔn)備工作呢爪膊?因?yàn)楹罄m(xù)還要執(zhí)行真正的inject
注入屬性方法权悟,最后會(huì)通過(guò)Spring的beanFacotry或者直接從cache中拿依賴對(duì)象,最后進(jìn)行屬性賦值推盛。至此峦阁,Spring @Autowired注解的處理流程就結(jié)束了。
四耘成、總結(jié)
- 綜上所述榔昔,針對(duì)@Autowired注解的處理流程主要核心為AutowiredAnnotationBeanPostProcessor后置處理器的MergedBeanDefinitionPostProcessor身份,它會(huì)去篩選出所有帶@Autowired注解的非靜態(tài)字段和非靜態(tài)方法作為候選者凿跳,最終再通過(guò)spring的bean工廠去獲取依賴的對(duì)象件豌,使用反射的技術(shù)完成注入
- I am a slow walker, but I never walk backwards.