欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?Autowired注解深入分析

 更新時(shí)間:2023年01月31日 15:43:11   作者:風(fēng)輕揚(yáng)777  
@Autowired注解是Spring中非常重要且常見(jiàn)的,接下來(lái)就簡(jiǎn)要的介紹一下它的用法。@Autowired默認(rèn)是通過(guò)set方法,按照類(lèi)型自動(dòng)裝配JavaBean,set方法可省略不寫(xiě),它主要是修飾在成員變量上

今天是正月初八,先祝大家新年快樂(lè)。前幾天遇見(jiàn)了一次Autowired注入失敗的問(wèn)題,所以找時(shí)間研究了一下相關(guān)的Spring源碼,分享一下。如果哪位大佬發(fā)現(xiàn)問(wèn)題,請(qǐng)幫忙反饋。
分享之前,先給一個(gè)小建議。Spring源碼龐大,其中的擴(kuò)展點(diǎn)眾多,貿(mào)然全篇吸收,很容易勸退。我的思路是想看哪部分知識(shí),就只看和這部分相關(guān)的,其他細(xì)節(jié),先放放,只捋主線(xiàn),這樣做,效率比較高。
下面進(jìn)入正文。
我寫(xiě)了一個(gè)非常簡(jiǎn)單的web工程,便于調(diào)試源碼。測(cè)試代碼如下:
Controller代碼

@RestController
public class HomeController {
    @Autowired
    private HomeService homeService;
    @RequestMapping("/index")
    public String index() {
        return homeService.testService();
    }
}

Service代碼

//接口
public interface HomeService {
    String testService();
}
//實(shí)現(xiàn)類(lèi)
@Service
public class HomeServiceImpl implements HomeService{
    @Override
    public String testService(){
        return "I'm a test service";
    }
}

我們的目的是:把HomeService通過(guò)Autowired注解注入HomeController中,從而在index方法中實(shí)現(xiàn)對(duì)HomeService方法的內(nèi)部調(diào)用。
我們想探究的是:
為什么我們給字段加一個(gè)Autowired注解,Spring就知道需要給bean注入這個(gè)字段對(duì)應(yīng)的服務(wù)呢?
想知道為什么Spring知道給HomeController中注入另外一個(gè)bean,我們肯定就得看HomeController,這個(gè)bean是怎么創(chuàng)建出來(lái)的。
直接看AbstractAutowireCapableBeanFactory方法的doCreateBean方法,里面是Spring創(chuàng)建bean的邏輯。有2個(gè)地方和Autowired有關(guān)系。
我去掉了無(wú)關(guān)邏輯,只留下了和Autowired有關(guān)系的邏輯
1、applyMergedBeanDefinitionPostProcessors()
2、populateBean(beanName, mbd, instanceWrapper)

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		//遍歷所有的beanPostProcessor,尋找bean中被Autowired注解修飾的成員變量
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}
		Object exposedObject = bean;
		try {
			//給bean填充其他bean。對(duì)應(yīng)到我們的demo,就是給HomeController注入HomeService。
			populateBean(beanName, mbd, instanceWrapper);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}
	}

applyMergedBeanDefinitionPostProcessors方法,這個(gè)方法的作用是尋找HomeController的字段里,有沒(méi)有哪個(gè)字段添加了Autowired注解。

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
		for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
			processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
		}
	}

這段邏輯實(shí)際上是在遍歷容器中的BeanPostProcessor,然后執(zhí)行BeanPostProcessor中的邏輯,我們需要關(guān)注的是AutowiredAnnotationBeanPostProcessor,這個(gè)類(lèi)是實(shí)現(xiàn)Autowired邏輯的核心類(lèi),大家重點(diǎn)關(guān)注。

@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}

可以看到邏輯很少。第一行代碼就是找HomeController中被Autowired注解修飾的字段。
我們進(jìn)入findAutowiringMetadata中看一下。其中有一個(gè)內(nèi)部方法調(diào)用是buildAutowiringMetadata(clazz),我們?cè)龠M(jìn)入這個(gè)方法

private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
			//尋找字段上有被Autowired注解修飾的字段
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
				//static修飾的靜態(tài)字段不能進(jìn)行注入
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});
		return InjectionMetadata.forElements(elements, clazz);
	}

以上邏輯執(zhí)行完,我們就拿到了待注入的homeService的元信息對(duì)象,即:InjectionMetadata。
上面邏輯中有一行代碼,是判斷字段是否被static修飾,如果是,不可以被注入。
小節(jié)一下。
繞了這么一圈,其實(shí)很簡(jiǎn)單。就是Spring在創(chuàng)建HomeController這個(gè)bean的時(shí)候,會(huì)檢測(cè)其成員字段有沒(méi)有被Autowired修飾。
對(duì)應(yīng)到我們的demo代碼,我們知道了HomeController中有一個(gè)被Autowired注解修飾的字段HomeService。
接下來(lái),之后就是給HomeController注入這個(gè)HomeService。

populateBean(beanName, mbd, instanceWrapper);我們進(jìn)入這個(gè)方法看一下。

重要邏輯是:pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)還是AutowiredAnnotationBeanPostProcessor。注入的核心邏輯是

@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		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;
	}

只有兩行邏輯。
第一行:
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);這行代碼,我們上面說(shuō)到過(guò),第一次執(zhí)行時(shí),是為了尋找被Autowired修飾字段的元信息。然后放入了緩存中。這一次再執(zhí)行,我們就是要從緩存中拿到待注入的數(shù)據(jù)元信息InjectionMetadata。
第二行:
開(kāi)始注入。
先看第一行代碼的具體邏輯,獲取待注入的數(shù)據(jù)元信息。

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		//構(gòu)建緩存key
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		//第一次執(zhí)行,緩存中沒(méi)有數(shù)據(jù),先構(gòu)建,然后放入緩存。第二次執(zhí)行有數(shù)據(jù),直接從緩存中獲取
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
			synchronized (this.injectionMetadataCache) {
				metadata = this.injectionMetadataCache.get(cacheKey);
				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
					if (metadata != null) {
						metadata.clear(pvs);
					}
					//第一次執(zhí)行,緩存中不存在,先放入緩存。
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}

可以看到,上面有一個(gè)雙檢鎖的設(shè)計(jì),應(yīng)該是防止并發(fā)情況下,多次創(chuàng)建InjectionMetadata對(duì)象。
再看第二行的具體邏輯,metadata.inject(bean, beanName, pvs);核心邏輯在A(yíng)utowiredAnnotationBeanPostProcessor的626行,inject方法中。重點(diǎn)就是下面的代碼。找到這個(gè)待注入的homeService對(duì)象,然后用反射注入到HomeController中。

@Override
		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
			else {
			//到Spring容器中尋找待注入的bean
				value = resolveFieldValue(field, bean, beanName);
			}
			//找到之后,反射注入
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}

找的過(guò)程,就是先從Spring子容器中找,然后到Spring父容器中找,不詳細(xì)說(shuō)了,感興趣的,可以繼續(xù)跟進(jìn)resolveFieldValue方法看。其實(shí),大部分的Autowired注入失敗,都是由于未在Spring容器中找到待注入的bean。所以Spring容器的加載過(guò)程很重要,一定要讀讀那部分源碼。
至此,Autowired的主要執(zhí)行流程,我們就分享完了。
總結(jié)下來(lái)就是:看bean中是否有被Autowired注解修飾的成員變量。如果有,從Spring容器中找到這個(gè)成員變量對(duì)應(yīng)的bean,注入進(jìn)去。

到此這篇關(guān)于Java Autowired注解深入分析的文章就介紹到這了,更多相關(guān)Java Autowired內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis?+?Java攔截器實(shí)現(xiàn)用戶(hù)匿名和非匿名訪(fǎng)問(wèn)

    Redis?+?Java攔截器實(shí)現(xiàn)用戶(hù)匿名和非匿名訪(fǎng)問(wèn)

    本文主要介紹了Redis?+?Java攔截器實(shí)現(xiàn)用戶(hù)匿名和非匿名訪(fǎng)問(wèn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 網(wǎng)關(guān)Gateway過(guò)濾器的使用詳解

    網(wǎng)關(guān)Gateway過(guò)濾器的使用詳解

    Gateway網(wǎng)關(guān)的過(guò)濾器分為兩種,一種是局部過(guò)濾器,一種是全局過(guò)濾器,過(guò)濾器就是過(guò)濾一些請(qǐng)求,在這里,全局過(guò)濾器的作用是處理一切進(jìn)入網(wǎng)關(guān)的請(qǐng)求和微服務(wù)響應(yīng),與GatewayFilter的作用一樣,本文給大家介紹網(wǎng)關(guān)Gateway過(guò)濾器的使用,感興趣的朋友一起看看吧
    2022-07-07
  • SpringBoot中處理日期的兩種方式小結(jié)

    SpringBoot中處理日期的兩種方式小結(jié)

    本文主要介紹了SpringBoot中處理日期的兩種方式小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java 兩種延時(shí)thread和timer詳解及實(shí)例代碼

    Java 兩種延時(shí)thread和timer詳解及實(shí)例代碼

    這篇文章主要介紹了Java 兩種延時(shí)thread和timer詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • 通過(guò)RedisTemplate連接多個(gè)Redis過(guò)程解析

    通過(guò)RedisTemplate連接多個(gè)Redis過(guò)程解析

    這篇文章主要介紹了通過(guò)RedisTemplate連接多個(gè)Redis過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • java開(kāi)發(fā)工作中對(duì)InheritableThreadLocal使用思考

    java開(kāi)發(fā)工作中對(duì)InheritableThreadLocal使用思考

    這篇文章主要為大家介紹了java開(kāi)發(fā)工作中對(duì)InheritableThreadLocal使用思考詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 利用Spring IOC技術(shù)實(shí)現(xiàn)用戶(hù)登錄驗(yàn)證機(jī)制

    利用Spring IOC技術(shù)實(shí)現(xiàn)用戶(hù)登錄驗(yàn)證機(jī)制

    這篇文章主要為大家詳細(xì)介紹了Spring IOC技術(shù)實(shí)現(xiàn)用戶(hù)登錄驗(yàn)證機(jī)制的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • JavaEE中struts2實(shí)現(xiàn)文件上傳下載功能實(shí)例解析

    JavaEE中struts2實(shí)現(xiàn)文件上傳下載功能實(shí)例解析

    這篇文章主要為大家詳細(xì)介紹了JavaEE中struts2實(shí)現(xiàn)文件上傳下載功能實(shí)例,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Java 基礎(chǔ)語(yǔ)法

    Java 基礎(chǔ)語(yǔ)法

    這篇文章主要介紹了Java 基礎(chǔ)語(yǔ)法,Java 是一門(mén)面向?qū)ο蟮慕忉屝途幊陶Z(yǔ)言,面向?qū)ο?意味著我們應(yīng)該把一個(gè) Java 程序看作一系列對(duì)象的集合,我們的工作就是構(gòu)建這些對(duì)象,并通過(guò)調(diào)用彼此的方法來(lái)讓各種對(duì)象協(xié)同工作,解決實(shí)際的問(wèn)題,下面文章內(nèi)容需要的朋友可以參考下一
    2021-11-11
  • Nacos作為配置中心注冊(cè)監(jiān)聽(tīng)器方法

    Nacos作為配置中心注冊(cè)監(jiān)聽(tīng)器方法

    本文主要討論Nacos作為配置中心時(shí),其中配置內(nèi)容發(fā)生更改時(shí),我們的應(yīng)用程序能夠做的事。一般使用監(jiān)聽(tīng)器來(lái)實(shí)現(xiàn)這步操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-02-02

最新評(píng)論