色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

@Autowired的使用及源碼分析

Android編程精選 ? 來源:CSDN技術社區 ? 作者:CSDN技術社區 ? 2022-06-14 17:04 ? 次閱讀
@Autowired使用

構造函數注入

publicClassOuter{
privateInnerinner;
@Autowired
publicOuter(Innerinner){
this.inner=inner;
}
}

屬性注入

publicClassOuter{
@Autowired
privateInnerinner;
}

方法注入

publicClassOuter{
privateInnerinner;
publicInnergetInner(){
returninner;
}
@Autowired
publicvoidsetInner(Innerinner){
this.inner=inner;
}
}

目前絕大部分的代碼都使用第2、第3種。第1種在bean實例化時完成,而第2、第3種的實現原理都是一樣的,在屬性填充時完成。本篇將介紹第二第三種的是實現原理

在開始之前,如果我們自己設計@Autowired,我們應該怎么實現?我想做法還是比較簡單的

  • 通過反射查找bean的class下所有注解了@Autowired的字段和方法
  • 獲取到字段,通過getBean(字段)獲取到對應bean,然后再通過反射調用field的set將bean注入

@Autowired源碼分析

AutowiredAnnotationBeanPostProcessor

該類是@Autowired的具體實現類,先預覽一下類方法

c1d7d10e-e657-11ec-ba43-dac502259ad0.png

發現實際有機會介入bean的創建操作只有可能是后置處理器,用于后置處理的有3個方法,其中一個過時不用,分別是postProcessMergedBeanDefinitionpostProcessProperties后置處理,我們再看一下這2個方法的具體代碼

publicclassAutowiredAnnotationBeanPostProcessorextendsInstantiationAwareBeanPostProcessorAdapter
implementsMergedBeanDefinitionPostProcessor,PriorityOrdered,BeanFactoryAware{

...

@Override
publicvoidpostProcessMergedBeanDefinition(RootBeanDefinitionbeanDefinition,ClassbeanType,StringbeanName){
//1.尋找bean中所有被@Autowired注釋的屬性,并將屬性封裝成InjectedElement類型
InjectionMetadatametadata=findAutowiringMetadata(beanName,beanType,null);
metadata.checkConfigMembers(beanDefinition);
}

...

@Override
publicPropertyValuespostProcessProperties(PropertyValuespvs,Objectbean,StringbeanName){
//1.尋找通過@Autowired注解的屬性或者方法
InjectionMetadatametadata=findAutowiringMetadata(beanName,bean.getClass(),pvs);
try{
//2.注入
metadata.inject(bean,beanName,pvs);
}
catch(BeanCreationExceptionex){
throwex;
}
catch(Throwableex){
thrownewBeanCreationException(beanName,"Injectionofautowireddependenciesfailed",ex);
}
returnpvs;
}

...
}

跟我們的猜想是一樣的,首先先找出所有注解了@Autowired的屬性或者方法,然后進行注入,當然postProcessMergedBeanDefinition后置處理器的調用肯定是在postProcessProperties之前的,這里我們回顧一下spring bean的創建過程。

2個處理器我已用黃色標出

c2169da8-e657-11ec-ba43-dac502259ad0.png

1. 查找所有@Autowired

//尋找bean中所有被@Autowired注釋的屬性,并將屬性封裝成InjectedElement類型
InjectionMetadatametadata=findAutowiringMetadata(beanName,beanType,null);
privateInjectionMetadatafindAutowiringMetadata(StringbeanName,Classclazz,@NullablePropertyValuespvs){
//Fallbacktoclassnameascachekey,forbackwardscompatibilitywithcustomcallers.
//獲取緩存的key值,一般以beanName做key
StringcacheKey=(StringUtils.hasLength(beanName)?beanName:clazz.getName());
//Quickcheckontheconcurrentmapfirst,withminimallocking.
//從緩存中獲取metadata
InjectionMetadatametadata=this.injectionMetadataCache.get(cacheKey);
//檢測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);
}
}
}
returnmetadata;
}

可以看到spring依然在用緩存的方式提高性能,繼續跟蹤核心代碼buildAutowiringMetadata(clazz)

privateInjectionMetadatabuildAutowiringMetadata(finalClassclazz){
//查看clazz是否有Autowired注解
if(!AnnotationUtils.isCandidateClass(clazz,this.autowiredAnnotationTypes)){
returnInjectionMetadata.EMPTY;
}
//這里需要注意AutowiredFieldElement,AutowiredMethodElement均繼承了InjectionMetadata.InjectedElement
//因此這個列表是可以保存注解的屬性和被注解的方法的
Listelements=newArrayList<>();
ClasstargetClass=clazz;

//1.通過dowhile循環,遞歸的往直接繼承的父類尋找@Autowired
do{
finalListcurrElements=newArrayList<>();

//2.通過反射,獲取所有屬性,doWithLocalFields則是循環的對每個屬性應用以下匿名方法
ReflectionUtils.doWithLocalFields(targetClass,field->{
//判斷當前field屬性是否含有@Autowired的注解
MergedAnnotationann=findAutowiredAnnotation(field);
if(ann!=null){
//返回該屬性在類中的修飾符,如果等于static常量,則拋出異常,@Autowired不允許注解在靜態屬性上
if(Modifier.isStatic(field.getModifiers())){
if(logger.isInfoEnabled()){
logger.info("Autowiredannotationisnotsupportedonstaticfields:"+field);
}
return;
}
//@Autowired有required屬性,獲取required的值,默認為true
booleanrequired=determineRequiredStatus(ann);
//3.將field封裝成InjectedElement,并添加到集合中,這里用的是AutowiredFieldElement
currElements.add(newAutowiredFieldElement(field,required));
}
});

//4.@Autowired可以注解在方法上
ReflectionUtils.doWithLocalMethods(targetClass,method->{
MethodbridgedMethod=BridgeMethodResolver.findBridgedMethod(method);
if(!BridgeMethodResolver.isVisibilityBridgeMethodPair(method,bridgedMethod)){
return;
}
MergedAnnotationann=findAutowiredAnnotation(bridgedMethod);
if(ann!=null&&method.equals(ClassUtils.getMostSpecificMethod(method,clazz))){
if(Modifier.isStatic(method.getModifiers())){
if(logger.isInfoEnabled()){
logger.info("Autowiredannotationisnotsupportedonstaticmethods:"+method);
}
return;
}
if(method.getParameterCount()==0){
if(logger.isInfoEnabled()){
logger.info("Autowiredannotationshouldonlybeusedonmethodswithparameters:"+
method);
}
}
booleanrequired=determineRequiredStatus(ann);
PropertyDescriptorpd=BeanUtils.findPropertyForMethod(bridgedMethod,clazz);
//5.將方法封裝成InjectedElement,并添加到集合中,這里用的是AutowiredMethodElement
currElements.add(newAutowiredMethodElement(method,required,pd));
}
});

elements.addAll(0,currElements);
//返回直接繼承的父類
targetClass=targetClass.getSuperclass();
}
//如果父類不為空則需要把父類的@Autowired屬性或方法也找出
while(targetClass!=null&&targetClass!=Object.class);
//6.newInjectionMetadata(clazz,elements),將找到的所有的待注入屬性或方法生成metadata返回
returnInjectionMetadata.forElements(elements,clazz);
}
  • 外層 do … while … 的循環被用于遞歸的查找父類的@Autowired屬性或方法

  • 通過反射的方式獲取到所有屬性并循環驗證每一個屬性是否被@Autowired注解

  • 將查找到包含@Autowired注解的filed封裝成AutowiredFieldElement,加入到列表中

  • 循環查找在方法上的注解

  • 將找到的方法封裝成AutowiredMethodElement,并加入列表

這里需要特別強調一點,InjectedElementAutowiredFieldElementAutowiredMethodElement所繼承,他們都有各自的inject函數,實現各自的注入。因此改ArrayList elements是擁有2種類型的屬性

c24a2466-e657-11ec-ba43-dac502259ad0.png
  • 將找到的所有元素列表和clazz作為參數生成metadata數據返回

2. 注入

//注入
metadata.inject(bean,beanName,pvs);
publicvoidinject(Objecttarget,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{
//獲取所有需要被注入的元素
CollectioncheckedElements=this.checkedElements;
CollectionelementsToIterate=
(checkedElements!=null?checkedElements:this.injectedElements);
//迭代的元素不為空
if(!elementsToIterate.isEmpty()){
for(InjectedElementelement:elementsToIterate){
if(logger.isTraceEnabled()){
logger.trace("Processinginjectedelementofbean'"+beanName+"':"+element);
}
//循環注入,這里有可能是AutowiredFieldElement也可能AutowiredMethodElement,因此調用的inject是2個不同的方法
element.inject(target,beanName,pvs);
}
}
}

利用for循環,遍歷剛剛我們查到到的elements列表,進行注入。

在上面有特別提醒,這里的element有可能是AutowiredFieldElement類型、或AutowiredMethodElement類型。各自代表@Autowired注解在屬性上、以及注解在方法上的2種不同元素。因此他們調用的element.inject(target, beanName, pvs);也是不一樣的

2.1 字段注入(AutowiredFieldElement)
privateclassAutowiredFieldElementextendsInjectionMetadata.InjectedElement{
@Override
protectedvoidinject(Objectbean,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{
Fieldfield=(Field)this.member;
Objectvalue;
if(this.cached){
value=resolvedCachedArgument(beanName,this.cachedFieldValue);
}
else{
//專門用于注入的包裝類,包裝構造函數參數,方法參數或字段
DependencyDescriptordesc=newDependencyDescriptor(field,this.required);
//設置class
desc.setContainingClass(bean.getClass());
//需要被自動注入的beanNames,這里只有可能=1,方法注入時才有可能為多個
SetautowiredBeanNames=newLinkedHashSet<>(1);
Assert.state(beanFactory!=null,"NoBeanFactoryavailable");
TypeConvertertypeConverter=beanFactory.getTypeConverter();//獲取類型轉換器
try{
//通過beanFactory獲取屬性對應的值,比如需要調用getBean("b")獲取依賴的屬性單例,并且通過自動轉型轉為需要的類型
value=beanFactory.resolveDependency(desc,beanName,autowiredBeanNames,typeConverter);
}
catch(BeansExceptionex){
thrownewUnsatisfiedDependencyException(null,beanName,newInjectionPoint(field),ex);
}
synchronized(this){
if(!this.cached){
if(value!=null||this.required){
this.cachedFieldValue=desc;
//注冊依賴,
registerDependentBeans(beanName,autowiredBeanNames);
//因為是屬性注入,因此這里只有可能等于1
if(autowiredBeanNames.size()==1){
StringautowiredBeanName=autowiredBeanNames.iterator().next();
if(beanFactory.containsBean(autowiredBeanName)&&
beanFactory.isTypeMatch(autowiredBeanName,field.getType())){
//緩存當前value
this.cachedFieldValue=newShortcutDependencyDescriptor(
desc,autowiredBeanName,field.getType());
}
}
}
else{
this.cachedFieldValue=null;
}
this.cached=true;
}
}
}
if(value!=null){
//通過反射,將value值設置到bean中
ReflectionUtils.makeAccessible(field);
field.set(bean,value);
}
}
}

上方大部分的工作都在做待注入bean的獲取以及類型的轉換,如果深究下去可以再把spring Ioc講一遍,但是核心還是getBean(字段)獲取到對應bean…我們這里就關心核心的語句,就是這2句

if(value!=null){
//通過反射,將value值設置到bean中
ReflectionUtils.makeAccessible(field);
field.set(bean,value);
}

spring通過反射的方式,調用field的set進行屬性的注入

2.2 方法注入(AutowiredMethodElement)
privateclassAutowiredMethodElementextendsInjectionMetadata.InjectedElement{


@Override
protectedvoidinject(Objectbean,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{
if(checkPropertySkipping(pvs)){
return;
}
//@Autowired標注在方法上
Methodmethod=(Method)this.member;
Object[]arguments;
if(this.cached){
//Shortcutforavoidingsynchronization...
//有緩存
arguments=resolveCachedArguments(beanName);
}
else{
//沒緩存,直接獲取方法上所有的參數
intargumentCount=method.getParameterCount();
arguments=newObject[argumentCount];
DependencyDescriptor[]descriptors=newDependencyDescriptor[argumentCount];
SetautowiredBeans=newLinkedHashSet<>(argumentCount);
Assert.state(beanFactory!=null,"NoBeanFactoryavailable");
TypeConvertertypeConverter=beanFactory.getTypeConverter();
//循環所有參數
for(inti=0;inewMethodParameter(method,i);
DependencyDescriptorcurrDesc=newDependencyDescriptor(methodParam,this.required);
currDesc.setContainingClass(bean.getClass());
descriptors[i]=currDesc;
try{
//通過beanFactory,獲取代注入的bean,并進行類型轉換
Objectarg=beanFactory.resolveDependency(currDesc,beanName,autowiredBeans,typeConverter);
if(arg==null&&!this.required){
arguments=null;
break;
}
arguments[i]=arg;
}
catch(BeansExceptionex){
thrownewUnsatisfiedDependencyException(null,beanName,newInjectionPoint(methodParam),ex);
}
}
synchronized(this){
if(!this.cached){
if(arguments!=null){
DependencyDescriptor[]cachedMethodArguments=Arrays.copyOf(descriptors,arguments.length);
//注冊依賴
registerDependentBeans(beanName,autowiredBeans);
//如果自動注入的個數=參數個數,則緩存
if(autowiredBeans.size()==argumentCount){
Iteratorit=autowiredBeans.iterator();
Class[]paramTypes=method.getParameterTypes();
for(inti=0;iif(beanFactory.containsBean(autowiredBeanName)&&
beanFactory.isTypeMatch(autowiredBeanName,paramTypes[i])){
//緩存
cachedMethodArguments[i]=newShortcutDependencyDescriptor(
descriptors[i],autowiredBeanName,paramTypes[i]);
}
}
}
//緩存方法
this.cachedMethodArguments=cachedMethodArguments;
}
else{
this.cachedMethodArguments=null;
}
this.cached=true;
}
}
}
if(arguments!=null){
try{
//反射調用注入方法,將獲取到的所有bean作為參數
ReflectionUtils.makeAccessible(method);
method.invoke(bean,arguments);
}
catch(InvocationTargetExceptionex){
throwex.getTargetException();
}
}
}

}

這里與屬性注入最大的區別在于,@Autowired注解在方法上,方法可以擁有多個參數,因此這里需要通過循環將一個個獲取,而獲取bean的方式于上面一樣,本質都是通過getBean獲取。

而核心語句還是2句

//反射調用注入方法,將獲取到的所有bean作為參數
ReflectionUtils.makeAccessible(method);
method.invoke(bean,arguments);

與屬性注入不同的是,當@Autowired注解在方法上,例如我們注解在setter方法上,則只需要直接調用該setter方法將參數數組傳入即可以,即使用invoke觸發方法,具體屬性賦值的過程在setter方法中由用戶自行編寫

原文標題:談談 @Autowired 的實現原理

文章出處:【微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 源碼
    +關注

    關注

    8

    文章

    639

    瀏覽量

    29185
  • 注解
    +關注

    關注

    0

    文章

    18

    瀏覽量

    2672

原文標題:談談 @Autowired 的實現原理

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    分享主成分分析源碼

    一個主成分分析源碼
    發表于 05-13 11:00

    u-boot源碼分析與移植 適合初學者

    u-boot源碼分析與移植u-boot源碼分析與移植
    發表于 05-21 12:57

    Linux內核源碼之我見——內核源碼分析方法

    的代碼高手。透過閱讀Linux內核代碼的方式,我們學習到的不光是內核相關的知識,在我看來更具價值的是學習和體會它們的編程技巧以及對計算機的理解。我也是通過一個項目接觸了Linux內核源碼分析,從源碼
    發表于 05-11 07:00

    鴻蒙源碼分析系列(總目錄) | 給HarmonyOS源碼逐行加上中文注釋

    同步更新。鴻蒙源碼分析系列篇|- 鴻蒙內核源碼分析 |-圖解鴻蒙源碼逐行注釋分析(內存概念篇)
    發表于 11-20 11:24

    uCOS2源碼分析

    uCOS2源碼分析1-BSP部分-第4季第2部分視頻課程 互聯網課程品牌《朱...
    發表于 07-20 06:48

    互斥量源碼分析測試

    文章目錄互斥量源碼分析測試參考資料:RTT官網文檔關鍵字:分析RT-Thread源碼、stm32、RTOS、互斥量。互斥量在其他書籍中的名稱:mutex :互斥鎖,互斥量,互斥體。從信
    發表于 08-24 06:01

    uCOS2源碼分析

    uCOS2源碼分析3-RTOS核心代碼視頻課程-第4季第4部分 互聯網課程品...
    發表于 01-12 06:28

    nucleus plus源碼分析下載

    |Nucleus PLUS源碼分析Nucleus PLUS Internals 相關文檔Nucleus PLUS 參考手冊,Accelerated Technology編著,描述如何操作
    發表于 07-07 15:18 ?36次下載

    基于stm32_TFT液晶屏顯示源碼分析

    本文檔詳細的對stm32TFT液晶屏顯示源碼進行分析
    發表于 08-29 14:22 ?13次下載

    基于stm32TFT液晶屏顯示源碼分析

    基于stm32TFT液晶屏顯示源碼分析,感興趣的小伙伴們可以瞧一瞧。
    發表于 11-18 17:50 ?66次下載

    UCOS-III OS_CPU_PendSVHandler源碼分析

    UCOS-III OS_CPU_PendSVHandler源碼分析
    發表于 08-28 10:48 ?13次下載

    uboot源碼分析,思路還算清晰

    uboot源碼分析,思路還算清晰
    發表于 10-24 15:25 ?19次下載
    uboot<b class='flag-5'>源碼</b><b class='flag-5'>分析</b>,思路還算清晰

    Java反射的工作原理和源碼分析

    Java反射的工作原理和源碼分析
    發表于 07-08 15:11 ?14次下載
    Java反射的工作原理和<b class='flag-5'>源碼</b><b class='flag-5'>分析</b>

    十二個Pixhawk源碼筆記分析資源下載

    十二個Pixhawk源碼筆記分析資源下載
    發表于 04-02 09:20 ?4次下載
    十二個Pixhawk<b class='flag-5'>源碼</b>筆記<b class='flag-5'>分析</b>資源下載

    epoll源碼分析

    個函數進行源碼分析源碼來源 由于epoll的實現內嵌在內核中,直接查看內核源碼的話會有一些無關代碼影響閱讀。為此在GitHub上寫的簡化版TCP/IP協議棧,里面實現了epoll邏
    的頭像 發表于 11-13 11:49 ?1036次閱讀
    epoll<b class='flag-5'>源碼</b><b class='flag-5'>分析</b>
    主站蜘蛛池模板: xxxxx中国明星18| 久久精品动漫99精品动漫| 老师的丝袜脚| 无码骚夜夜精品| gay吊粗大双龙| 精品国产免费第一区二区| 色姐妹久久综合在线av| 最近的2019中文字幕国语版| 国产看午夜精品理论片| 全彩acg无翼乌火影忍者| 中文字幕日本在线mv视频精品| 国产日韩久久久精品影院首页| 啪啪啪社区| 4438成人情人网站| 娇妻被朋友玩得呻吟在线电影| 私人玩物在线观看| yellow免费影视大全| 伦理片在线线手机版韩国免费6| 亚洲欧美日韩国产精品26u| 高清欧美性猛交xxxx黑人猛交| 男同志china免费视频| 亚洲伊人久久一次| 国产视频精品免费| 手机毛片在线观看| 阿片在线播放| 捏揉舔水插按摩师| 2017最新伦理伦理片67| 久久伦理影院| 一线高清视频在线播放| 精品成人在线视频| 亚洲 国产 日韩 欧美 在线| 国产国拍精品AV在线观看| 日韩精品欧美亚洲高清有无| xxx免费观看| 青青青青草原国产免费| china中国gay偷拍| 欧美zzzoooxxx| swag合集120部| 秋霞在线观看视频一区二区三区| 999久久狠狠免费精品| 免费精品一区二区三区AA片|