Java?Autowired注解深入分析
今天是正月初八,先祝大家新年快樂。前幾天遇見了一次Autowired注入失敗的問題,所以找時間研究了一下相關(guān)的Spring源碼,分享一下。如果哪位大佬發(fā)現(xiàn)問題,請幫忙反饋。
分享之前,先給一個小建議。Spring源碼龐大,其中的擴展點眾多,貿(mào)然全篇吸收,很容易勸退。我的思路是想看哪部分知識,就只看和這部分相關(guān)的,其他細(xì)節(jié),先放放,只捋主線,這樣做,效率比較高。
下面進(jìn)入正文。
我寫了一個非常簡單的web工程,便于調(diào)試源碼。測試代碼如下:
Controller代碼
@RestController
public class HomeController {
@Autowired
private HomeService homeService;
@RequestMapping("/index")
public String index() {
return homeService.testService();
}
}
Service代碼
//接口
public interface HomeService {
String testService();
}
//實現(xiàn)類
@Service
public class HomeServiceImpl implements HomeService{
@Override
public String testService(){
return "I'm a test service";
}
}
我們的目的是:把HomeService通過Autowired注解注入HomeController中,從而在index方法中實現(xiàn)對HomeService方法的內(nèi)部調(diào)用。
我們想探究的是:
為什么我們給字段加一個Autowired注解,Spring就知道需要給bean注入這個字段對應(yīng)的服務(wù)呢?
想知道為什么Spring知道給HomeController中注入另外一個bean,我們肯定就得看HomeController,這個bean是怎么創(chuàng)建出來的。
直接看AbstractAutowireCapableBeanFactory方法的doCreateBean方法,里面是Spring創(chuàng)建bean的邏輯。有2個地方和Autowired有關(guān)系。
我去掉了無關(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。對應(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方法,這個方法的作用是尋找HomeController的字段里,有沒有哪個字段添加了Autowired注解。
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
這段邏輯實際上是在遍歷容器中的BeanPostProcessor,然后執(zhí)行BeanPostProcessor中的邏輯,我們需要關(guān)注的是AutowiredAnnotationBeanPostProcessor,這個類是實現(xiàn)Autowired邏輯的核心類,大家重點關(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中看一下。其中有一個內(nèi)部方法調(diào)用是buildAutowiringMetadata(clazz),我們再進(jìn)入這個方法
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的元信息對象,即:InjectionMetadata。
上面邏輯中有一行代碼,是判斷字段是否被static修飾,如果是,不可以被注入。
小節(jié)一下。
繞了這么一圈,其實很簡單。就是Spring在創(chuàng)建HomeController這個bean的時候,會檢測其成員字段有沒有被Autowired修飾。
對應(yīng)到我們的demo代碼,我們知道了HomeController中有一個被Autowired注解修飾的字段HomeService。
接下來,之后就是給HomeController注入這個HomeService。
populateBean(beanName, mbd, instanceWrapper);我們進(jìn)入這個方法看一下。
重要邏輯是:pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
對應(yīng)的實現(xiàn)類還是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);這行代碼,我們上面說到過,第一次執(zhí)行時,是為了尋找被Autowired修飾字段的元信息。然后放入了緩存中。這一次再執(zhí)行,我們就是要從緩存中拿到待注入的數(shù)據(jù)元信息InjectionMetadata。
第二行:
開始注入。
先看第一行代碼的具體邏輯,獲取待注入的數(shù)據(jù)元信息。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
//構(gòu)建緩存key
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
//第一次執(zhí)行,緩存中沒有數(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;
}
可以看到,上面有一個雙檢鎖的設(shè)計,應(yīng)該是防止并發(fā)情況下,多次創(chuàng)建InjectionMetadata對象。
再看第二行的具體邏輯,metadata.inject(bean, beanName, pvs);核心邏輯在AutowiredAnnotationBeanPostProcessor的626行,inject方法中。重點就是下面的代碼。找到這個待注入的homeService對象,然后用反射注入到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);
}
}
找的過程,就是先從Spring子容器中找,然后到Spring父容器中找,不詳細(xì)說了,感興趣的,可以繼續(xù)跟進(jìn)resolveFieldValue方法看。其實,大部分的Autowired注入失敗,都是由于未在Spring容器中找到待注入的bean。所以Spring容器的加載過程很重要,一定要讀讀那部分源碼。
至此,Autowired的主要執(zhí)行流程,我們就分享完了。
總結(jié)下來就是:看bean中是否有被Autowired注解修飾的成員變量。如果有,從Spring容器中找到這個成員變量對應(yīng)的bean,注入進(jìn)去。
到此這篇關(guān)于Java Autowired注解深入分析的文章就介紹到這了,更多相關(guān)Java Autowired內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis?+?Java攔截器實現(xiàn)用戶匿名和非匿名訪問
本文主要介紹了Redis?+?Java攔截器實現(xiàn)用戶匿名和非匿名訪問,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
java開發(fā)工作中對InheritableThreadLocal使用思考
這篇文章主要為大家介紹了java開發(fā)工作中對InheritableThreadLocal使用思考詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
利用Spring IOC技術(shù)實現(xiàn)用戶登錄驗證機制
這篇文章主要為大家詳細(xì)介紹了Spring IOC技術(shù)實現(xiàn)用戶登錄驗證機制的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10
JavaEE中struts2實現(xiàn)文件上傳下載功能實例解析
這篇文章主要為大家詳細(xì)介紹了JavaEE中struts2實現(xiàn)文件上傳下載功能實例,感興趣的小伙伴們可以參考一下2016-05-05

