Java超詳細(xì)分析@Autowired原理
@Autowired使用
構(gòu)造函數(shù)注入
public Class Outer { private Inner inner; @Autowired public Outer(Inner inner) { this.inner = inner; } }
屬性注入
public Class Outer { @Autowired private Inner inner; }
方法注入
public Class Outer { private Inner inner; public Inner getInner() { return inner; } @Autowired public void setInner(Inner inner) { this.inner = inner; } }
目前絕大部分的代碼都使用第2、第3種。第1種在bean實(shí)例化時(shí)完成,而第2、第3種的實(shí)現(xiàn)原理都是一樣的,在屬性填充時(shí)完成。本篇將介紹第二第三種的是實(shí)現(xiàn)原理
在開始之前,如果我們自己設(shè)計(jì)@Autowired
,我們應(yīng)該怎么實(shí)現(xiàn)?我想做法還是比較簡(jiǎn)單的
- 通過反射查找bean的class下所有注解了@Autowired的字段和方法
- 獲取到字段,通過getBean(字段)獲取到對(duì)應(yīng)bean,然后再通過反射調(diào)用field的set將bean注入
@Autowired源碼分析
AutowiredAnnotationBeanPostProcessor
類
該類是@Autowired
的具體實(shí)現(xiàn)類,先預(yù)覽一下類方法
發(fā)現(xiàn)實(shí)際有機(jī)會(huì)介入bean的創(chuàng)建操作只有可能是后置處理器,用于后置處理的有3個(gè)方法,其中一個(gè)過時(shí)不用,分別是postProcessMergedBeanDefinition
、postProcessProperties
后置處理,我們?cè)倏匆幌逻@2個(gè)方法的具體代碼
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { ... @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { // 1. 尋找bean中所有被@Autowired注釋的屬性,并將屬性封裝成InjectedElement類型 InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); } ... @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 1. 尋找通過@Autowired注解的屬性或者方法 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // 2. 注入 metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; } ... }
跟我們的猜想是一樣的,首先先找出所有注解了@Autowired
的屬性或者方法,然后進(jìn)行注入,當(dāng)然postProcessMergedBeanDefinition
后置處理器的調(diào)用肯定是在postProcessProperties
之前的,這里我們回顧一下spring bean
的創(chuàng)建過程。
2個(gè)處理器我已用黃色標(biāo)出
1.查找所有@Autowired
// 尋找bean中所有被@Autowired注釋的屬性,并將屬性封裝成InjectedElement類型 InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. // 獲取緩存的key值,一般以beanName做key String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. // 從緩存中獲取metadata InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); // 檢測(cè)metadata是否需要更新 if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } // 通過clazz類,查找所有@Autowired的屬性或者方法,并封裝成InjectionMetadata類型 metadata = buildAutowiringMetadata(clazz); // 將metadata加入緩存 this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; }
可以看到spring依然在用緩存的方式提高性能,繼續(xù)跟蹤核心代碼buildAutowiringMetadata(clazz)
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { // 查看clazz是否有Autowired注解 if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { return InjectionMetadata.EMPTY; } // 這里需要注意AutowiredFieldElement,AutowiredMethodElement均繼承了InjectionMetadata.InjectedElement // 因此這個(gè)列表是可以保存注解的屬性和被注解的方法的 List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); Class<?> targetClass = clazz; // 1. 通過do while循環(huán),遞歸的往直接繼承的父類尋找@Autowired do { final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); // 2. 通過反射,獲取所有屬性,doWithLocalFields則是循環(huán)的對(duì)每個(gè)屬性應(yīng)用以下匿名方法 ReflectionUtils.doWithLocalFields(targetClass, field -> { // 判斷當(dāng)前field屬性是否含有@Autowired的注解 MergedAnnotation<?> ann = findAutowiredAnnotation(field); if (ann != null) { // 返回該屬性在類中的修飾符,如果等于static常量,則拋出異常,@Autowired不允許注解在靜態(tài)屬性上 if (Modifier.isStatic(field.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static fields: " + field); } return; } // @Autowired有required屬性,獲取required的值,默認(rèn)為true boolean required = determineRequiredStatus(ann); // 3. 將field封裝成InjectedElement,并添加到集合中,這里用的是AutowiredFieldElement currElements.add(new AutowiredFieldElement(field, required)); } }); // 4. @Autowired可以注解在方法上 ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static methods: " + method); } return; } if (method.getParameterCount() == 0) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation should only be used on methods with parameters: " + method); } } boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); // 5. 將方法封裝成InjectedElement,并添加到集合中,這里用的是AutowiredMethodElement currElements.add(new AutowiredMethodElement(method, required, pd)); } }); elements.addAll(0, currElements); // 返回直接繼承的父類 targetClass = targetClass.getSuperclass(); } // 如果父類不為空則需要把父類的@Autowired屬性或方法也找出 while (targetClass != null && targetClass != Object.class); // 6. new InjectionMetadata(clazz, elements),將找到的所有的待注入屬性或方法生成metadata返回 return InjectionMetadata.forElements(elements, clazz); }
- 外層
do … while …
的循環(huán)被用于遞歸的查找父類的@Autowired
屬性或方法 - 通過反射的方式獲取到所有屬性并循環(huán)驗(yàn)證每一個(gè)屬性是否被
@Autowired
注解 - 將查找到包含
@Autowired
注解的filed封裝成AutowiredFieldElement
,加入到列表中 - 循環(huán)查找在方法上的注解
- 將找到的方法封裝成
AutowiredMethodElement
,并加入列表
這里需要特別強(qiáng)調(diào)一點(diǎn),InjectedElement
被AutowiredFieldElement
、AutowiredMethodElement
所繼承,他們都有各自的inject函數(shù),實(shí)現(xiàn)各自的注入。因此改ArrayList elements
是擁有2種類型的屬性
- 將找到的所有元素列表和clazz作為參數(shù)生成metadata數(shù)據(jù)返回
2. 注入
// 注入 metadata.inject(bean, beanName, pvs); public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 獲取所有需要被注入的元素 Collection<InjectedElement> checkedElements = this.checkedElements; Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); // 迭代的元素不為空 if (!elementsToIterate.isEmpty()) { for (InjectedElement element : elementsToIterate) { if (logger.isTraceEnabled()) { logger.trace("Processing injected element of bean '" + beanName + "': " + element); } // 循環(huán)注入,這里有可能是AutowiredFieldElement也可能AutowiredMethodElement,因此調(diào)用的inject是2個(gè)不同的方法 element.inject(target, beanName, pvs); } } }
利用for循環(huán),遍歷剛剛我們查到到的elements列表,進(jìn)行注入。
在上面有特別提醒,這里的element有可能是AutowiredFieldElement
類型、或AutowiredMethodElement
類型。各自代表@Autowired
注解在屬性上、以及注解在方法上的2種不同元素。因此他們調(diào)用的element.inject(target, beanName, pvs);
也是不一樣的
2.1 字段注入(AutowiredFieldElement)
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; if (this.cached) { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else { // 專門用于注入的包裝類,包裝構(gòu)造函數(shù)參數(shù),方法參數(shù)或字段 DependencyDescriptor desc = new DependencyDescriptor(field, this.required); // 設(shè)置class desc.setContainingClass(bean.getClass()); // 需要被自動(dòng)注入的beanNames,這里只有可能 = 1,方法注入時(shí)才有可能為多個(gè) Set<String> autowiredBeanNames = new LinkedHashSet<>(1); Assert.state(beanFactory != null, "No BeanFactory available"); TypeConverter typeConverter = beanFactory.getTypeConverter();// 獲取類型轉(zhuǎn)換器 try { // 通過beanFactory獲取屬性對(duì)應(yīng)的值,比如需要調(diào)用getBean("b")獲取依賴的屬性單例,并且通過自動(dòng)轉(zhuǎn)型轉(zhuǎn)為需要的類型 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } synchronized (this) { if (!this.cached) { if (value != null || this.required) { this.cachedFieldValue = desc; // 注冊(cè)依賴, registerDependentBeans(beanName, autowiredBeanNames); // 因?yàn)槭菍傩宰⑷?,因此這里只有可能等于1 if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { // 緩存當(dāng)前value this.cachedFieldValue = new ShortcutDependencyDescriptor( desc, autowiredBeanName, field.getType()); } } } else { this.cachedFieldValue = null; } this.cached = true; } } } if (value != null) { // 通過反射,將value值設(shè)置到bean中 ReflectionUtils.makeAccessible(field); field.set(bean, value); } } }
上方大部分的工作都在做待注入bean的獲取以及類型的轉(zhuǎn)換,如果深究下去可以再把spring Ioc講一遍,但是核心還是getBean(字段)獲取到對(duì)應(yīng)bean…我們這里就關(guān)心核心的語(yǔ)句,就是這2句
if (value != null) { // 通過反射,將value值設(shè)置到bean中 ReflectionUtils.makeAccessible(field); field.set(bean, value); }
spring通過反射的方式,調(diào)用field的set進(jìn)行屬性的注入
2.2 方法注入(AutowiredMethodElement)
private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { if (checkPropertySkipping(pvs)) { return; } // @Autowired標(biāo)注在方法上 Method method = (Method) this.member; Object[] arguments; if (this.cached) { // Shortcut for avoiding synchronization... // 有緩存 arguments = resolveCachedArguments(beanName); } else { // 沒緩存,直接獲取方法上所有的參數(shù) int argumentCount = method.getParameterCount(); arguments = new Object[argumentCount]; DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount]; Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount); Assert.state(beanFactory != null, "No BeanFactory available"); TypeConverter typeConverter = beanFactory.getTypeConverter(); // 循環(huán)所有參數(shù) for (int i = 0; i < arguments.length; i++) { MethodParameter methodParam = new MethodParameter(method, i); DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required); currDesc.setContainingClass(bean.getClass()); descriptors[i] = currDesc; try { // 通過beanFactory,獲取代注入的bean,并進(jìn)行類型轉(zhuǎn)換 Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter); if (arg == null && !this.required) { arguments = null; break; } arguments[i] = arg; } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex); } } synchronized (this) { if (!this.cached) { if (arguments != null) { DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length); // 注冊(cè)依賴 registerDependentBeans(beanName, autowiredBeans); // 如果自動(dòng)注入的個(gè)數(shù) = 參數(shù)個(gè)數(shù),則緩存 if (autowiredBeans.size() == argumentCount) { Iterator<String> it = autowiredBeans.iterator(); Class<?>[] paramTypes = method.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { String autowiredBeanName = it.next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { // 緩存 cachedMethodArguments[i] = new ShortcutDependencyDescriptor( descriptors[i], autowiredBeanName, paramTypes[i]); } } } // 緩存方法 this.cachedMethodArguments = cachedMethodArguments; } else { this.cachedMethodArguments = null; } this.cached = true; } } } if (arguments != null) { try { // 反射調(diào)用注入方法,將獲取到的所有bean作為參數(shù) ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } }
這里與屬性注入最大的區(qū)別在于,@Autowired
注解在方法上,方法可以擁有多個(gè)參數(shù),因此這里需要通過循環(huán)將一個(gè)個(gè)獲取,而獲取bean的方式于上面一樣,本質(zhì)都是通過getBean獲取。
而核心語(yǔ)句還是2句
// 反射調(diào)用注入方法,將獲取到的所有bean作為參數(shù) ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments);
與屬性注入不同的是,當(dāng)@Autowired
注解在方法上,例如我們注解在setter方法上,則只需要直接調(diào)用該setter方法將參數(shù)數(shù)組傳入即可以,即使用invoke觸發(fā)方法,具體屬性賦值的過程在setter方法中由用戶自行編寫
到此這篇關(guān)于Java超詳細(xì)分析@Autowired原理的文章就介紹到這了,更多相關(guān)Java @Autowired內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談SpringBoot之開啟數(shù)據(jù)庫(kù)遷移的FlyWay使用
這篇文章主要介紹了淺談SpringBoot之開啟數(shù)據(jù)庫(kù)遷移的FlyWay使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01java調(diào)用shell腳本及注意事項(xiàng)說明
這篇文章主要介紹了java調(diào)用shell腳本及注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06java實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07妙用Java8中的Function接口消滅if...else
在開發(fā)過程中經(jīng)常會(huì)使用if...else...進(jìn)行判斷拋出異常、分支處理等操作。這些if...else...充斥在代碼中嚴(yán)重影響了代碼代碼的美觀,本文就妙用Java8中的Function接口消滅if...else,感興趣的可以了解一下2022-01-01Java修改eclipse中web項(xiàng)目的server部署路徑問題
這篇文章主要介紹了Java修改eclipse中web項(xiàng)目的server部署路徑,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11Java分頁(yè)查詢--分頁(yè)顯示(實(shí)例講解)
下面小編就為大家?guī)硪黄狫ava分頁(yè)查詢--分頁(yè)顯示(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08Synchronized?和?ReentrantLock?的實(shí)現(xiàn)原理及區(qū)別
這篇文章主要介紹了Synchronized?和?ReentrantLock?的實(shí)現(xiàn)原理及區(qū)別,文章為榮啊主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09