@Autowired注解實現原理

在討論代碼細節之前,我們再來了解下基礎知識。Spring管理可用于整個應用程序的Java對象bean。他們所在的Spring容器,被稱為應用程序上下文。這意味著我們不需要處理他們的生命周期(初始化,銷毀)。該任務由此容器來完成。另外,該上下文具有入口點,在Web應用程序中,是dispatcher servlet。容器(也就是該上下文)會在它那里被啟動并且所有的bean都會被注入。

說的再清楚點,請看<context:annotation-config />的定義:

<xsd:element name="annotation-config">
<xsd:annotation>
<xsd:documentation><![CDATA[
Activates various annotations to be detected in bean classes: Spring's @Required and
@Autowired, as well as JSR 250's @PostConstruct, @PreDestroy and @Resource (if available),
JAX-WS's @WebServiceRef (if available), EJB 3's @EJB (if available), and JPA's
@PersistenceContext and @PersistenceUnit (if available). Alternatively, you may
choose to activate the individual BeanPostProcessors for those annotations.
Note: This tag does not activate processing of Spring's @Transactional or EJB 3's
@TransactionAttribute annotation. Consider the use of the <tx:annotation-driven>
tag for that purpose.
See javadoc for org.springframework.context.annotation.AnnotationConfigApplicationContext
for information on code-based alternatives to bootstrapping annotation-driven support.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
可以看出 : 類內部的注解,如:@Autowired、@Value、@Required、@Resource以及EJB和WebSerivce相關的注解,是容器對Bean對象實例化和依賴注入時,通過容器中注冊的Bean后置處理器處理這些注解的。

所以配置了上面這個配置(<context:component-scan>假如有配置這個,那么就可以省略<context:annotation-config />)后,將隱式地向Spring容器注冊AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor以及這4個專門用于處理注解的Bean后置處理器。

當 Spring 容器啟動時,AutowiredAnnotationBeanPostProcessor 將掃描 Spring 容器中所有 Bean,當發現 Bean 中擁有@Autowired 注解時就找到和其匹配(默認按類型匹配)的 Bean,并注入到對應的地方中去。 源碼分析如下:

通過org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor可以實現依賴自動注入。通過這個類來處理@Autowired和@Value這倆Spring注解。它也可以管理JSR-303的@Inject注解(如果可用的話)。在AutowiredAnnotationBeanPostProcessor構造函數中定義要處理的注解:

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
...
/**
* Create a new AutowiredAnnotationBeanPostProcessor
* for Spring's standard {@link Autowired} annotation.
* <p>Also supports JSR-330's {@link javax.inject.Inject} annotation, if available.
*/
@SuppressWarnings("unchecked")
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
...
}
之后,有幾種方法來對@Autowired注解進行處理。

第一個,private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz)解析等待自動注入類的所有屬性。它通過分析所有字段和方法并初始化org.springframework.beans.factory.annotation.InjectionMetadata類的實例來實現。

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
Class<?> targetClass = clazz;
do {
final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
//分析所有字段
ReflectionUtils.doWithLocalFields(targetClass, field -> {
//findAutowiredAnnotation(field)此方法后面會解釋
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
//分析所有方法
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));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
//返回一個InjectionMetadata初始化的對象實例
return new InjectionMetadata(clazz, elements);
}
...
/**
* 'Native' processing method for direct calls with an arbitrary target instance,
* resolving all of its fields and methods which are annotated with {@code @Autowired}.
* @param bean the target instance to process
* @throws BeanCreationException if autowiring failed
*/
public void processInjection(Object bean) throws BeanCreationException {
Class<?> clazz = bean.getClass();
InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null);
try {
metadata.inject(bean, null, null);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
"Injection of autowired dependencies failed for class [" + clazz + "]", ex);
}
}
InjectionMetadata類包含要注入的元素的列表。注入是通過Java的API Reflection (Field set(Object obj, Object value) 或Method invoke(Object obj,Object ... args)方法完成的。此過程直接在AutowiredAnnotationBeanPostProcessor的方法中調用public void processInjection(Object bean) throws BeanCreationException。它將所有可注入的bean檢索為InjectionMetadata實例,并調用它們的inject()方法。

public class 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()) {
boolean debug = logger.isDebugEnabled();
for (InjectedElement element : elementsToIterate) {
if (debug) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
}
//看下面靜態內部類的方法
element.inject(target, beanName, pvs);
}
}
}
...
public static abstract class InjectedElement {
protected final Member member;
protected final boolean isField;
...
/**
* 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();
}
}
}
...
}
}
AutowiredAnnotationBeanPostProcessor類中的另一個重要方法是private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao)。它通過分析屬于一個字段或一個方法的所有注解來查找@Autowired注解。如果未找到@Autowired注解,則返回null,字段或方法也就視為不可注入。

@Nullable
private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
if (ao.getAnnotations().length > 0) {
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
if (attributes != null) {
return attributes;
}
}
}
return null;
}
在上面的文章中,我們看到了Spring中自動注入過程。通過整篇文章可以看到,這種依賴注入是一種便捷易操作方式(可以在字段以及方法上完成),也促使我們逐漸在拋棄XML配置文件。還增強了代碼的可讀性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/539383.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/539383.shtml
英文地址,請注明出處:http://en.pswp.cn/news/539383.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

獲取freemarker處理后的內容

相信很多人都用過freemarker&#xff0c;或做視圖&#xff0c;或模板&#xff0c;或生成靜態文件等,但是有多少人做過這樣的應用&#xff0c;通過模板后&#xff0c;不是要輸出靜態的內容&#xff0c;而是直接在代碼中獲取處理模板后的內容&#xff0c;研究了下API,freemarker里…

c4.5算法python實現_算法:用Python實現—最優化算法

今天給大家分享一下算法&#xff0c;用python來實現最優化算法。廢話不多說&#xff0c;直接上代碼&#xff1a;一、二分法函數詳見rres&#xff0c;此代碼使該算法運行了兩次def asdf(x): rres8*x**3-2*x**2-7*x3 return rresi2left0right1while i>0 : i i-1 …

comsol臨時文件夾中有不支持的字符_文件名中不能包含的字符

文件名是為了方便人們區分計算機中的不同文件&#xff0c;而給每個文件設定一個指定的名稱。由文件主名和擴展名組成。DOS操作系統規定文件名由文件主名和擴展名組成&#xff0c;文件主名由1~8個字符組成&#xff0c;擴展名由1~3個字符組成&#xff0c;主名和擴展名之間由一個小…

linux 星號 通配符,如何在bash中轉義通配符/星號字符?

簡短的回答像其他人所說的那樣 - 你應該總是引用變量來防止奇怪的行為。所以使用echo“$ foo”代替echo $ foo。長期回答我確實認為這個例子值得進一步解釋&#xff0c;因為它的表面看起來比它看起來更多。我可以看到你的困惑在哪里&#xff0c;因為在你運行你的第一個例子后&a…

PYTHON面試

大部分的面試問題&#xff0c;有最近要找事的老鐵嗎&#xff1f;python語法以及其他基礎部分可變與不可變類型&#xff1b; 淺拷貝與深拷貝的實現方式、區別&#xff1b;deepcopy如果你來設計&#xff0c;如何實現&#xff1b; __new__() 與 __init__()的區別&#xff1b; 你知…

vs怎么更改編譯的堆空間_再見吧 buildSrc, 擁抱 Composing builds 提升 Android 編譯速度...

前言長期以來困擾我們的一個問題就是構建速度&#xff0c;AndroidStudio 的構建速度嚴重影響 Android 開發者的工作效率&#xff0c;尤其是更新一個版本號&#xff0c;導致整個項目重新構建&#xff0c;在網絡慢的情況下&#xff0c;這是無法忍受的。buildSrc 這種方式&#xf…

java map的遍歷

轉載地址&#xff1a;http://www.cnblogs.com/shenliang123/archive/2012/08/28/2660705.html -------------------------------------------------------------------------------------------------------------------- java中的遍歷 import java.util.Collection; import j…

python循環for...in_python循環while和forin實例

python 循環while和for in簡單實例#!/uer/bin/env python# _*_ coding: utf-8 _*_lucknumber 5b 0while b <3:print(guss count:,b)a int(input(you guse number))if a > lucknumber:print (youaerbiger)elif a lucknumber:print (youare righet)break #跳出這個層級…

android懸浮功能實現,Android實現系統級懸浮按鈕

本文實例為大家分享了Android系統級懸浮按鈕的具體代碼&#xff0c;供大家參考&#xff0c;具體內容如下具體的需求1、就是做一個系統級的懸浮按鈕&#xff0c;就像iPhone 桌面的那個懸浮按鈕效果一樣&#xff0c;能隨意拖動&#xff0c;并且手一放開&#xff0c;懸浮按鈕就自動…

oracle decode_錯過血虧!一文搞懂Oracle鎖相關視圖及相關操作

本文主要研究鎖的相關視圖&#xff0c;以及鎖的相關操作&#xff0c;通過視圖查鎖的問題。 一、v$transaction視圖第一個視圖是v$transaction&#xff0c;就是Oracle數據庫所有活動的事務數&#xff0c;所有活動的事務每一個活動的事務在這里有一行。v$transactionXIDUSN表示當…

Linux文件系統與命令行

什么是命令行? 接收鍵盤命令并將其傳給操作系統執行的程序(用于輸入和管理命令的程序),統稱命令行,也叫: Shell&#xff0c;幾乎所有Linux發行版都提供了一個 Shell 程序,叫做: Bash (Bourne-Again Shell, 因為最初的 Shell 是由 Steve Bourne 編寫的原始 Unix 程序, Again 表…

freeMarker 遍歷 list,map,listmap

List List<String> clientSourceDatanew ArrayList<String>();clientSourceData.add("field字段");clientSourceData.add("title標題");ftl&#xff1a; <#if clientSourceData?exists><#list clientSourceData as key> <tr&g…

qtableview不選中_如何選中/取消選中QTableView并觸發setData()

我有一個自定義的QTableModel&#xff0c;我在PyQt中使用QTableView顯示它。我有一些字段設置為可檢查&#xff0c;我想添加“全部檢查”和“不檢查”按鈕。我覺得應該有一種方法可以使setData()從代碼中被調用&#xff0c;這樣檢查狀態就會改變&#xff0c;就像我已經用setDat…

android 自定義菜單欄,GitHub - earthWo/AndroidBottomNavigation: android 底部菜單欄,自定義樣式,自定義菜單數量,添加滾動動畫和水波紋動畫...

AndroidBottomNavigation截圖使用方法gradle:compile com.whitelife.library:library:1.0.1maven:com.whitelife.librarylibrary1.0pomandroid:id"id/bottom_navigation"android:layout_width"match_parent"android:layout_height"56dp"android:…

windows怎么打開python_windows怎么打開Python

Windows中運行Python的兩種運行方式認識編程環境 1 直接命令行啟用Python。當然&#xff0c;如果直接在cmd中輸入python&#xff0c;需要在windows中的path環境變量中做好設置。 此時&#xff0c;cmd中運行python就可以出現 “>>>” 符號。意味著python進入了交互運行…

sqldeveloper創建賬號_用oralce 自帶工具sql developer 創建表空間,用戶,權限

用oralce 自帶工具sql developer 創建/*第1步&#xff1a;創建臨時表空間 */create temporary tablespace hudongtemptablespacetempfile E:\worksubject\WY-Honda_Ess\Oracle11g\hudongtemptablespace.dbfsize 50mautoextend onnext 50m maxsize 20480mextent management loc…

AOE網與關鍵路徑簡介

前面我們說過的拓撲排序主要是為解決一個工程能否順序進行的問題&#xff0c;但有時我們還需要解決工程完成需要的最短時間問題。如果我們要對一個流程圖獲得最短時間&#xff0c;就必須要分析它們的拓撲關系&#xff0c;并且找到當中最關鍵的流程&#xff0c;這個流程的時間就…

Java 集合體系詳解——List體系有序集合

引言 面向對象語言對事物的體現必然是以對象的形式&#xff0c;Java工程師為了方便多多個對象的操作&#xff0c;就對對象進行存儲&#xff0c;集合就是存儲對象的一種方式&#xff0c;他們的底層都是基于不同的數據結構。當然集合和數組一樣都是容器&#xff0c;數組也是可以存…

android 定義固定數組,Android 圖片數組定義和讀取

位置&#xff1a;packages/apps/Launcher21、圖片數組定義、資源讀取如果有多張圖片&#xff0c;這些圖片的使用與順序無關&#xff0c;可以采取這種方式。drawable-nodpi中有3張圖片&#xff0c;wallpaper_1.jpg、wallpaper_2.jpg、wallpaper_3.jpgXML中定義數組IDwallpaper_1…

alert閃一下就沒了_尾部貫穿式鍍鉻銀飾條除了丑,還能閃瞎眼

尾部貫穿式鍍鉻銀飾條&#xff0c;在2010年代成為諸多汽車品牌車型爭相采用的新世紀新標配&#xff0c;配以雙邊排氣&#xff0c;讓整個車尾看起來層次感強烈&#xff0c;視覺收窄&#xff0c;幾十萬的奧迪A8L有&#xff0c;十幾萬的斯柯達速派有&#xff0c;A級車有&#xff0…