詳解Spring注解@Autowired的實(shí)現(xiàn)原理和使用方法
如何使用@Autowired注解?
將@Autowired
注解應(yīng)用于構(gòu)造函數(shù),如以下示例所示:
@Component public class BeanConfig{ @Autowired private BeanConfig beanConfig; @Autowired public BeanConfig(BeanConfig beanConfig) { this.beanConfig = beanConfig; } }
直接應(yīng)用于字段是我們使用最多的方式,但是從代碼層面使用構(gòu)造函數(shù)注入會(huì)更好。因?yàn)闃?gòu)造器注入的方式,能夠保證注入的依賴(lài)不可變,并確保需要的依賴(lài)不為空。此外,構(gòu)造器注入的依賴(lài)總是能夠在返回客戶(hù)端(組件)代碼的時(shí)候保證完全初始化的狀態(tài)。
此外,還有以下幾種不太常用的方法,見(jiàn)下面的代碼:
@Autowired private List<BeanConfig> beanConfigList; @Autowired private Set<BeanConfig> beanConfigSet; @Autowired private Map<String, BeanConfig> beanConfigMap;
雖然我們經(jīng)常使用這個(gè)注解,但是我們真的了解它的作用嗎?
首先從它的作用域來(lái)看,其實(shí)這個(gè)注解是屬于容器配置的Spring
注解,其他屬于容器配置注解:@Required
, @Primary
, @Qualifier
等。
其次,我們可以直接看字面意思,autowire
,這個(gè)詞的意思就是自動(dòng)裝配的意思。
自動(dòng)裝配是什么意思?這個(gè)詞的本意是指在一些行業(yè)中用機(jī)器代替人自動(dòng)完成一些需要裝配的任務(wù)。在Spring
的世界里,自動(dòng)組裝是指使用我們需要這個(gè)bean
的class
自動(dòng)組裝Spring
容器中的bean
。
所以這個(gè)注解作用的就是自動(dòng)將Spring
容器中的bean
和我們需要這個(gè)bean
一起使用的類(lèi)組裝起來(lái)。
接下來(lái),讓我們看看這個(gè)注解背后工作的原理。
如何實(shí)現(xiàn)@Autowired 注解?
Java注解實(shí)現(xiàn)的核心技術(shù)是反射。讓我們通過(guò)一些例子和自己實(shí)現(xiàn)一個(gè)注解來(lái)了解它的工作原理。
我們拿到target
之后就可以用反射給他實(shí)現(xiàn)一個(gè)邏輯,這種邏輯在這些方法本身的邏輯之外,這讓我們想起proxy、aop等知識(shí),我們相當(dāng)于為這些方法做了一個(gè)邏輯增強(qiáng)。
其實(shí)注解的實(shí)現(xiàn)主要邏輯大概就是這個(gè)思路??偨Y(jié)一下一般步驟如下:
- 使用反射機(jī)制獲取類(lèi)的
Class
對(duì)象。 - 通過(guò)這個(gè)類(lèi)對(duì)象,可以得到它的每一個(gè)方法方法,或者字段等。
Method
、Field
等類(lèi)提供了類(lèi)似getAnnotation
的方法來(lái)獲取某個(gè)字段的所有注解。- 拿到注解后,我們可以判斷該注解是否是我們要實(shí)現(xiàn)的注解,如果是,則實(shí)現(xiàn)注解邏輯。
下面我們來(lái)實(shí)現(xiàn)這個(gè)邏輯,代碼如下:
public void postProcessProperties() throws Exception { Class<BeanConfig> beanConfigClass = BeanConfig.class; BeanConfig instance = beanConfigClass.newInstance(); Field[] fields = beanConfigClass.getDeclaredFields(); for (Field field : fields) { // getAnnotation,判斷是否有Autowired Autowired autowired = field.getDeclaredAnnotation(Autowired.class); if (autowired != null) { String fileName = field.getName(); Class<?> declaringClass = field.getDeclaringClass(); Object bean = new Object(); field.setAccessible(true); field.set(bean, instance); } } }
從上面的實(shí)現(xiàn)邏輯不難發(fā)現(xiàn),借助Java反射,我們可以直接獲取一個(gè)類(lèi)中的所有方法,然后獲取方法上的注解。當(dāng)然,我們也可以獲取字段上的注解。在反射的幫助下,我們幾乎可以得到屬于一個(gè)類(lèi)的任何東西。這樣,我們自己簡(jiǎn)單做了一個(gè)實(shí)現(xiàn)。
知道了上面的知識(shí),我們就不難想到,上面的注解雖然簡(jiǎn)單,但是@Autowired
和他最大的區(qū)別應(yīng)該只是注解的實(shí)現(xiàn)邏輯,其他的如使用反射獲取注解等步驟應(yīng)該是相同的。
接下來(lái)我們看在Spring中,@Autowired
是如何實(shí)現(xiàn)的呢?
Spring中源碼解析
我們來(lái)看@Autowired
在Spring源碼中是如何定義注解的,如下:
package org.springframework.beans.factory.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { boolean required() default true; }
閱讀代碼可以看出,@Autowired
注解可以應(yīng)用于五類(lèi)構(gòu)造方法,普通方法、參數(shù)、字段、注解,其保留策略是在運(yùn)行時(shí)。
接下來(lái)我們看一Spring
對(duì)這個(gè)注解的邏輯實(shí)現(xiàn)。
在Spring
源碼中,@Autowired
注解位于包中org.springframework.beans.factory.annotation
。經(jīng)過(guò)分析不難發(fā)現(xiàn),Spring對(duì)自動(dòng)裝配注解的實(shí)現(xiàn)邏輯位于類(lèi):AutowiredAnnotationBeanPostProcessor
。
核心處理代碼如下:
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>(); // 需要處理的目標(biāo)類(lèi) Class<?> targetClass = clazz; do { final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>(); // 通過(guò)反射獲取本類(lèi)的所有字段,并遍歷每個(gè)字段 // 通過(guò)方法findAutowiredAnnotation遍歷每個(gè)字段使用的注解 // 如果用autowired修飾,返回autowired相關(guān)屬性 ReflectionUtils.doWithLocalFields(targetClass, field -> { AnnotationAttributes ann = findAutowiredAnnotation(field); // 檢查靜態(tài)方法上是否使用了自動(dòng)裝配注解 if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static fields: " + field); } return; } // 判斷是否指定了required boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); } }); //和上面的邏輯一樣,但是方法是通過(guò)反射來(lái)處理 ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { 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); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new AutowiredMethodElement(method, required, pd)); } }); // @Autowired 修飾的注解可能不止一個(gè) // 所以都加入到currElements容器中一起處理 elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); }
最后,此方法返回一個(gè)InjectionMetadata
包含所有autowire
注解的集合。
這個(gè)類(lèi)由兩部分組成:
public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) { this.targetClass = targetClass; this.injectedElements = elements; }
一個(gè)是我們要處理的目標(biāo)類(lèi),一個(gè)是elements
上面方法得到的集合。
有了目標(biāo)類(lèi)和所有需要注入的元素,我們就可以實(shí)現(xiàn)自動(dòng)裝配的依賴(lài)注入邏輯。實(shí)現(xiàn)方法如下。
@Override public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { 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; }
它調(diào)用的inject
方法就是定義在InjectionMetadata
。
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); } element.inject(target, beanName, pvs); } } } /** * Either this or {@link #getResourceToInject} needs to be overridden. */ protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable { if (this.isField) { Field field = (Field) this.member; ReflectionUtils.makeAccessible(field); field.set(target, getResourceToInject(target, requestingBeanName)); } else { if (checkPropertySkipping(pvs)) { return; } try { Method method = (Method) this.member; ReflectionUtils.makeAccessible(method); method.invoke(target, getResourceToInject(target, requestingBeanName)); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } }
上面代碼中,方法的參數(shù)getResourceToInject
是要注入的名稱(chēng),bean這個(gè)方法的作用是根據(jù)名稱(chēng)獲取bean
。
以上就是@Autowire
注解實(shí)現(xiàn)邏輯的完整解析。
下面是spring容器實(shí)現(xiàn)@Autowired
自動(dòng)注入的時(shí)序圖。
總結(jié)
本文講解了Spring中最常用的注解之一@Autowired
, 平時(shí)我們可能都是使用屬性注入的,但是后續(xù)建議大家慢慢改變習(xí)慣,使用構(gòu)造器注入。同時(shí)也講解了這個(gè)注解背后的實(shí)現(xiàn)原理,希望對(duì)大家有幫助。
以上就是詳解Spring注解@Autowired的實(shí)現(xiàn)原理和使用方法的詳細(xì)內(nèi)容,更多關(guān)于Spring注解@Autowired的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊(duì)列持久化(durable)及消息持久化
這篇文章主要介紹了rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊(duì)列持久化(durable)及消息持久化,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03SpringBoot啟動(dòng)后執(zhí)行方法的五種實(shí)現(xiàn)方式
本文介紹了SpringBoot中五種在項(xiàng)目啟動(dòng)后執(zhí)行方法的方式,包括實(shí)現(xiàn)CommandLineRunner和ApplicationRunner接口、實(shí)現(xiàn)ApplicationListener接口、使用@PostConstruct注解以及實(shí)現(xiàn)InitializingBean接口,每種方式都有其特點(diǎn)和適用場(chǎng)景2025-02-02模仿J2EE的session機(jī)制的App后端會(huì)話(huà)信息管理實(shí)例
下面小編就為大家分享一篇模仿J2EE的session機(jī)制的App后端會(huì)話(huà)信息管理實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11IntelliJ IDEA使用SVN分支的簡(jiǎn)單介紹
今天小編就為大家分享一篇關(guān)于IntelliJ IDEA使用SVN分支的簡(jiǎn)單介紹,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-10-10java并發(fā)編程專(zhuān)題(十一)----(JUC原子類(lèi))數(shù)組類(lèi)型詳解
這篇文章主要介紹了JAVA JUC原子類(lèi) 數(shù)組類(lèi)型詳解的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07并發(fā)編程ConcurrentLinkedQueue示例詳解
這篇文章主要為大家介紹了并發(fā)編程ConcurrentLinkedQueue使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Java讀取文件及基于正則表達(dá)式的獲取電話(huà)號(hào)碼功能詳解
這篇文章主要介紹了Java讀取文件及基于正則表達(dá)式的獲取電話(huà)號(hào)碼功能,結(jié)合實(shí)例形式詳細(xì)分析了正則匹配操作的相關(guān)語(yǔ)法及電話(huà)號(hào)碼匹配的原理與實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-09-09