Java @Autowired注解底層原理詳細分析
1.概念
@Autowired 是 Spring 提供的注解,默認的注入方式為 byType (按類型自動注入);@Autowired 注釋,它可以對類成員變量、方法及構(gòu)造函數(shù)進行標注,完成自動裝配的工作;通過 @Autowired的使用來消除 set ,get方法。
2.注入數(shù)據(jù)的注解
@Value
含義:通過set方式注入基本類型與String,set方法可以省略
語法:@Value("數(shù)據(jù)")/@Value("${key}")
位置:修飾屬性,set方法
注意:如果動態(tài)獲取必須指定加載資源文件 <context:property-placeholderlocation="classpath:msg.properties"> </context:property-placeholder>
@Autowired
替換:autowire屬性,自動裝配(按照類型裝配,通過set方法,且方法可以省略)
位置:修飾屬性,set方法
語法:@Autowired(required="true")
注意:
1.如果容器中沒有一個可以與之匹配且required屬性為true則會報異常
NoSuchBeanDefinitionException
2.如果容器中有多個可以類型可以與之匹配,則自動切換為按照名稱裝配
3.如果名稱也沒有匹配,則報異常NoUniqueBeanDefinitionException
3.@Autowired注解是如何實現(xiàn)的
@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注解可以應用在構(gòu)造方法,普通方法,參數(shù),字段,以及注解這五種類型的地方,它的保留策略是在運行時。
在Spring源代碼當中,Autowired注解位于包org.springframework.beans.factory.annotation之中,實現(xiàn)邏輯位于類:AutowiredAnnotationBeanPostProcessor之中。
核心處理代碼如下:
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
Class<?> targetClass = clazz;//需要處理的目標類
do {
final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
/*通過反射獲取該類所有的字段,并遍歷每一個字段,并通過方法findAutowiredAnnotation遍歷每一個字段的所用注解,并如果用autowired修飾了,則返回auotowired相關(guān)屬性*/
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {//校驗autowired注解是否用在了static方法上
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));
}
});
//和上面一樣的邏輯,但是是通過反射處理類的method
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修飾的注解可能不止一個,因此都加在currElements這個容器里面,一起處理
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}最后這個方法返回的就是包含所有帶有autowire注解修飾的一個InjectionMetadata集合。這個類由兩部分組成:
public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
this.targetClass = targetClass;
this.injectedElements = elements;
}一是處理的目標類,二就是上述方法獲取到的所以elements集合。
有了目標類,與所有需要注入的元素集合之后,就可以實現(xiàn)autowired的依賴注入邏輯了,實現(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)用的方法是InjectionMetadata中定義的inject方法,如下
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);
}
}
}其邏輯就是遍歷,然后調(diào)用inject方法,inject方法其實現(xiàn)邏輯如下:
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();
}
}
}inject也使用了反射技術(shù)并且依然是分成字段和方法去處理的。
到此這篇關(guān)于Java @Autowired注解底層原理詳細分析的文章就介紹到這了,更多相關(guān)Java @Autowired注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何查找YUM安裝的JAVA_HOME環(huán)境變量詳解
這篇文章主要給大家介紹了關(guān)于如何查找YUM安裝的JAVA_HOME環(huán)境變量的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2017-10-10
Java 網(wǎng)絡編程之 TCP 實現(xiàn)簡單的聊天系統(tǒng)
這篇文章主要介紹了Java 網(wǎng)絡編程之 TCP 實現(xiàn)簡單的聊天系統(tǒng),幫助大家更好的理解和學習Java 網(wǎng)絡編程,感興趣的朋友可以了解下2020-11-11
Spring整合Mybatis 掃描注解創(chuàng)建Bean報錯的解決方案
這篇文章主要介紹了Spring 整合Mybatis 掃描注解創(chuàng)建Bean報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
Java的StringBuilder在高性能場景下的正確用法
StringBuilder?對字符串的操作是直接改變字符串對象本身,而不是生成新的對象,所以新能開銷小.與StringBuffer相比StringBuilder的性能略高,StringBuilder則沒有保證線程的安全,從而性能略高于StringBuffer,需要的朋友可以參考下2023-05-05
Java+mysql本地圖片上傳數(shù)據(jù)庫及下載示例
本篇文章主要介紹了Java+mysql本地圖片上傳數(shù)據(jù)庫及下載示例,具有一定的參加價值,有興趣的可以了解一下。2017-01-01
java web中的servlet3 upload上傳文件實踐
這篇文章主要介紹了servlet3 upload上傳文件實踐,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-11-11
詳解基于SpringBoot使用AOP技術(shù)實現(xiàn)操作日志管理
這篇文章主要介紹了詳解基于SpringBoot使用AOP技術(shù)實現(xiàn)操作日志管理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-11-11

