Spring IOC(控制反轉)和DI(依賴注入)原理


一、Spring IoC容器和bean簡介

Spring Framework實現了控制反轉(IoC)原理,IoC也稱為依賴注入(DI)

這是一個過程,通過這個過程,對象定義它們的依賴關系,即它們使用的其他對象,只能通過構造函數參數,工廠方法的參數,或者在構造或從工廠方法返回后在對象實例上設置的屬性。

然后容器在創建bean時注入這些依賴項。這個過程基本上是相反的,因此稱為控制反轉(IoC),bean本身通過使用類的直接構造或諸如服務定位器模式之類的機制來控制其依賴關系的實例化或位置。

org.springframework.beans和 org.springframework.context包是Spring框架的IoC容器的基礎,該 BeanFactory 接口提供了一種能夠管理任何類型對象的高級配置機制。 ApplicationContext 它是 BeanFactory 的一個子接口, 它更容易與Spring的AOP功能集成、消息資源處理(用於國際化)、事件發布和特定於應用程序層的上下文,例如WebApplicationContext 在Web應用程序中使用的上下文。

簡而言之,它 BeanFactory 提供了配置框架和基本功能,並 ApplicationContext 添加了更多特定於企業的功能。

在Spring中,構成應用程序主干並由 Spring IoC 容器管理的對象稱為 bean。bean是一個由 Spring IoC 容器實例化、組裝和管理的對象。Bean及其之間的依賴關系反映在容器使用的配置元數據中。

二、容器概述

org.springframework.context.ApplicationContext 接口代表Spring IoC容器,負責實例化,配置和組裝上述bean。容器通過讀取配置元數據獲取有關要實例化,配置和組裝的對象的指令。配置元數據以XML,Java注釋或Java代碼表示。它允許表達組成應用程序的對象以及這些對象之間豐富的相互依賴性。

ApplicationContext是Spring的開箱即用的幾個接口實現 。在獨立應用程序中,通常會創建一個 ClassPathXmlApplicationContext 或的實例 FileSystemXmlApplicationContext。雖然XML是定義配置元數據的傳統格式,但您可以通過提供少量XML配置來指示容器使用Java注釋或代碼作為元數據格式,以聲明方式啟用對這些其他元數據格式的支持。

1 public void testAutoBeanName() {
2         ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("springStyle/declaration/auto/spring-config-bean-name.xml");
3         UserService proxy = (UserService) ctx.getBean("userService");
4         String ret = proxy.queryAllUser();
5         System.out.println("執行結果:" + ret);
6         proxy.saveUser("zhangsan");
7 }

下圖是Spring工作原理的高級視圖。您的應用程序類與配置元數據相結合,以便在ApplicationContext創建和初始化之后,擁有一個完全配置且可執行的系統或應用程序。

1. 配置元數據

Spring IoC容器使用一種配置元數據形式; 此配置元數據表示您作為應用程序開發人員如何告訴Spring容器在應用程序中實例化,配置和組裝對象。傳統上,配置元數據以簡單直觀的XML格式提供。

在Spring容器中使用其他形式的元數據的方式有:

  • 基於注釋的配置
  • 基於Java的配置

這些bean定義對應於構成應用程序的實際對象。通常,您定義服務層對象,數據訪問對象(DAO),表示對象(如Struts Action實例),基礎結構對象(如Hibernate SessionFactories,JMS Queues等)。通常,不會在容器中配置細粒度域對象,因為創建和加載域對象通常由DAO和業務邏輯負責。但是,您可以使用Spring與AspectJ的集成來配置在IoC容器控制之外創建的對象。

基於XML的配置元數據的基本結構:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 6 
 7   <bean id="..." class="...">
 8     <!-- collaborators and configuration for this bean go here -->
 9   </bean>
10 
11   <bean id="..." class="...">
12     <!-- collaborators and configuration for this bean go here -->
13   </bean>
14 
15   <!-- more bean definitions go here -->
16 
17 </beans>

2. 實例化容器

實例化Spring IoC容器非常簡單。提供給 ApplicationContext 構造函數位置路徑實際上是資源字符串,允許容器從各種外部資源(如本地文件系統,Java等)加載配置元數據 CLASSPATH

以下示例顯示了服務層對象 (services.xml)配置文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 6 
 7   <!-- services -->
 8 
 9   <bean id="petStore"
10         class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
11     <property name="accountDao" ref="accountDao"/>
12     <property name="itemDao" ref="itemDao"/>
13     <!-- additional collaborators and configuration for this bean go here -->
14   </bean>
15 
16   <!-- more bean definitions for services go here -->
17 
18 </beans>

以下示例顯示了數據訪問對象 daos.xml文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 6 
 7   <bean id="accountDao"
 8       class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapAccountDao">
 9     <!-- additional collaborators and configuration for this bean go here -->
10   </bean>
11 
12   <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapItemDao">
13     <!-- additional collaborators and configuration for this bean go here -->
14   </bean>
15 
16   <!-- more bean definitions for data access objects go here -->
17 
18 </beans>

在前面的示例中,服務層由類組成 PetStoreServiceImpl,並且類型 SqlMapAccountDao 和 SqlMapItemDao 的兩個數據訪問對象基於 iBatis 對象/關系映射框架。該property name元素是指JavaBean屬性的名稱,以及ref元素指的是另一個bean定義的名稱。id和ref元素之間的這種聯系表達了協作對象之間的依賴關系。

3. 使用容器

ApplicationContext 是高級工廠的接口,能夠維護不同bean及其依賴項的注冊表。使用該方法,T getBean(Stringname, Class<T> requiredType) 您可以檢索Bean的實例。

在 ApplicationContext 可以讀取bean定義並訪問它們,如下所示:

1     //創建和配置bean 
2     ApplicationContext context = new ClassPathXmlApplicationContext(new String [] { “services.xml”,“daos.xml” }); //檢索已配置的實例 
3     PetStoreServiceImpl service = context.getBean(“petStore”,PetStoreServiceImpl .class); //使用已配置的實例列表
4     userList service.getUsernameList();
5     

三、Bean概述

Spring IoC容器管理一個或多個bean。這些bean是使用您提供給容器的配置元數據創建的,例如,以XML <bean/>定義的形式 。

在容器本身內,這些bean定義表示為 BeanDefinition 對象,其中包含(以及其他信息)以下元數據:

  • 包限定的類名通常是正在定義的bean的實際實現類。

  • Bean行為配置元素,說明bean在容器中的行為方式(范圍,生命周期回調等)。

  • 引用bean執行其工作所需的其他bean; 這些引用也稱為 協作者 或 依賴項

  • 要在新創建的對象中設置的其他配置設置,例如,在管理連接池的Bean中使用的連接數,或池的大小限制。

每個bean都有一個或多個標識符。這些標識符在托管bean的容器中必須是唯一的。bean通常只有一個標識符,但如果它需要多個標識符,則額外的標識符可以被視為別名。

在基於XML的配置元數據中,使用 id和/或)name屬性指定bean標識符。該id屬性允許您指定一個id,並且因為它是一個真正的XML元素ID屬性,所以當其他元素引用id時,XML解析器可以進行一些額外的驗證。因此,它是指定bean標識符的首選方式。但是,XML規范確實限制了XML ID中合法的字符。這通常不是約束,但如果您需要使用其中一個特殊的XML字符,或者想要向bean引入其他別名,您還可以在name屬性中指定它們 ,用逗號(,),分號(;)分隔,或者是空格。

如果沒有顯式提供名稱或標識,則容器會為該bean生成唯一的名稱。但是,如果要通過名稱引用該bean,則必須通過使用ref元素或服務位置樣式查找來提供名稱。不提供名稱的動機與使用內部bean和自動裝配協作者有關。

bean定義本質上是用於創建一個或多個對象的方法。容器在被詢問時查看命名bean的方法,並使用由該bean定義封裝的配置元數據來創建(或獲取)實際對象。如果使用基於XML的配置元數據,則指定要在元素的class屬性中實例化的對象的類型(或類)。此 class 屬性在內部是實例 Class上的屬性 BeanDefinition,通常是必需的。可以通過Class以下兩種方式之一使用該屬性:

  • 通常,在容器本身通過反向調用其構造函數直接創建bean的情況下指定要構造的bean類,稍微等同於使用new運算符的Java代碼。

  • 指定包含實際的類static將被調用以創建該對象,在容器調用一個不常見的情況工廠方法static工廠的一類方法來創建bean。從調用static工廠方法返回的對象類型可以完全是同一個類或另一個類

四、依賴性

1. 依賴注入

依賴注入(DI)是一個過程,通過這個過程,對象定義它們的依賴關系,即它們使用的其他對象,只能通過構造函數參數,工廠方法的參數或在構造或返回對象實例后在對象實例上設置的屬性。從工廠方法。然后容器在創建bean時注入這些依賴項。這個過程基本上是相反的,因此名稱 Inversion of Control(IoC),bean本身通過使用類的直接構造或服務定位器模式來控制其依賴項的實例化或位置

使用DI原理的代碼更清晰,當對象提供其依賴項時,解耦更有效。該對象不查找其依賴項,也不知道依賴項的位置或類。因此,您的類變得更容易測試,特別是當依賴關系在接口或抽象基類上時,這允許在單元測試中使用存根或模擬實現。

 

DI存在兩個主要變體,基於構造函數的依賴注入基於Setter的依賴注入

基於構造函數 DI由容器調用具有多個參數的構造函數來完成,每個參數表示一個依賴項。調用static具有特定參數工廠方法來構造bean幾乎是等效的,本討論同樣處理構造函數和static工廠方法的參數。以下示例顯示了一個只能通過構造函數注入進行依賴注入的類。請注意,此類沒有什么 特別之處,它是一個POJO,它不依賴於容器特定的接口,基類或注釋。

基於setter DI是在調用無參數構造函數或無參數static工廠方法來實例化bean之后,通過容器調用bean上的setter方法來完成的

容器執行bean依賴性解析,如下所示:

  1. 使用ApplicationContext描述所有bean的配置元數據創建和初始化。可以通過XML,Java代碼或注釋指定配置元數據。

  2. 對於每個bean,如果使用的是依賴於普通構造函數的,那么它的依賴關系將以屬性,構造函數參數或static-factory方法的參數的形式表示。實際創建 bean 時,會將這些依賴項提供給bean 。

  3. 每個屬性或構造函數參數都是要設置的值的實際定義,或者是對容器中另一個bean的引用。

  4. 作為值的每個屬性或構造函數參數都從其指定格式轉換為該屬性或構造函數參數的實際類型。默認情況下,Spring能夠轉換成字符串格式提供給所有的內置類型,比如數值 intlong, Stringboolean,等。

Spring容器在創建容器時驗證每個bean的配置,包括驗證bean引用屬性是否引用有效bean。但是,在實際創建 bean之前,不會設置bean屬性本身創建容器時會創建單例作用域並設置為預先實例化(默認值)的Bean。范圍有singletonprototyperequestsessionglobal session否則,僅在請求時才創建bean。創建bean可能會導致創建bean的圖形,因為bean的依賴關系及其依賴關系(依此類推)被創建和分配。

2. 使用依賴depend-on

如果bean是另一個bean的依賴項,通常意味着將一個bean設置為另一個bean的屬性。 通常,您可以使用基於XML的配置元數據中的<ref />元素來完成此操作。 但是,有時bean之間的依賴關系不那么直接; 例如,需要觸發類中的靜態初始化程序,例如數據庫驅動程序注冊。 在初始化使用此元素的bean之前,depends-on屬性可以顯式強制初始化一個或多個bean。 以下示例使用depends-on屬性表示對單個bean的依賴關系:

1 <bean id="beanOne" class="ExampleBean" depends-on="manager"/>
2 
3 <bean id="manager" class="ManagerBean" />

要表示對多個bean的依賴關系,請提供bean名稱列表作為depends-on屬性的值,使用逗號,空格和分號作為有效分隔符:

1 <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
2   <property name="manager" ref="manager" />
3 </bean>
4 
5 <bean id="manager" class="ManagerBean" />
6 <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

3. Lazy-initialized beans

默認情況下,ApplicationContext實現會急切地創建和配置所有單例bean,作為初始化過程的一部分。 通常,這種預先實例化是可取的,因為配置或周圍環境中的錯誤是立即發現的,而不是幾小時甚至幾天后。 如果不希望出現這種情況,可以通過將bean定義標記為延遲初始化來阻止單例bean的預實例化。 延遲初始化的bean告訴IoC容器在第一次請求時創建bean實例,而不是在啟動時。

在XML中,此行為由<bean />元素上的lazy-init屬性控制; 例如:

1 <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
2 
3 <bean name="not.lazy" class="com.foo.AnotherBean"/>

當ApplicationContext使用前面的配置時,在ApplicationContext啟動時,不會急切地預先實例化名為lazy的bean,而是非常預先實例化not lazy bean。

但是,當延遲初始化的bean是未進行延遲初始化的單例bean的依賴項時,ApplicationContext會在啟動時創建延遲初始化的bean,因為它必須滿足單例的依賴關系。 惰性初始化的bean被注入到其他地方的單獨的bean中,而這個bean並不是惰性初始化的。

您還可以使用<beans />元素上的default-lazy-init屬性在容器級別控制延遲初始化; 例如:

1 <beans default-lazy-init="true">
2   <!-- no beans will be pre-instantiated... -->
3 </beans>

4. 自動裝配合作者 Autowiring collaborators

Spring容器可以自動連接協作bean之間的關系。您可以通過檢查ApplicationContext的內容,允許Spring自動為您的bean解析協作者(其他bean)。自動裝配具有以下優點:

  • 自動裝配可以顯着減少指定屬性或構造函數參數的需要。 
  • 自動裝配可以隨着對象的發展更新配置。例如,如果需要向類添加依賴項,則可以自動滿足該依賴項,而無需修改配置。因此,自動裝配在開發期間尤其有用,而不會在代碼庫變得更穩定時否定切換到顯式布線的選項。

使用基於XML的配置元數據時,可以使用<bean />元素的autowire屬性為bean定義指定autowire模式。自動裝配功能有五種模式:no(不自動裝載)、byName(根據實例變量名稱自動裝載)、byType(根據實例變量類型自動裝載)、constructor(根據構造方法參數類型自動裝載)、autodetect(byType方式和constructor方式結合)。您指定每個bean的自動裝配,因此可以選擇要自動裝配的那些。

自動裝配的限制和缺點:

  • property和constructor-arg設置中的顯式依賴項始終覆蓋自動裝配。您無法自動裝配所謂的簡單屬性,例如基元,字符串和類(以及此類簡單屬性的數組)。這種限制是按設計的。
  • 自動裝配不如顯式布線精確。盡管如上表所示,Spring會小心避免在可能產生意外結果的歧義的情況下進行猜測,但不再明確記錄Spring管理對象之間的關系。
  • 可能無法為可能從Spring容器生成文檔的工具提供接線信息。
  • 容器中的多個bean定義可以匹配setter方法或構造函數參數指定的類型以進行自動裝配。對於數組,集合或地圖,這不一定是個問題。但是,對於期望單個值的依賴關系,這種模糊性不是任意解決的。如果沒有可用的唯一bean定義,則拋出異常。

相關注解:

@Autowired注解的作用是由AutowiredAnnotationBeanPostProcessor實現的,查看該類的源碼會發現它實現了MergedBeanDefinitionPostProcessor接口,進而實現了接口中的postProcessMergedBeanDefinition方法,@Autowired注解正是通過這個方法實現注入類型的預解析,將需要依賴注入的屬性信息封裝到InjectionMetadata類中,InjectionMetadata類中包含了哪些需要注入的元素及元素要注入到哪個目標類中。

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

Spring容器在啟動的時候會執行AbstractApplicationContext類的refresh方法,refresh方法中registerBeanPostProcessors(beanFactory)完成了對AutowiredAnnotationBeanPostProcessor的注冊。

 1     @Override
 2     public void refresh() throws BeansException, IllegalStateException {
 3         synchronized (this.startupShutdownMonitor) {
 4             // Prepare this context for refreshing.
 5             prepareRefresh();
 6 
 7             // Tell the subclass to refresh the internal bean factory.
 8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 9 
10             // Prepare the bean factory for use in this context.
11             prepareBeanFactory(beanFactory);
12 
13             try {
14                 // Allows post-processing of the bean factory in context subclasses.
15                 postProcessBeanFactory(beanFactory);
16 
17                 // Invoke factory processors registered as beans in the context.
18                 invokeBeanFactoryPostProcessors(beanFactory);
19 
20                 // Register bean processors that intercept bean creation.
21                 registerBeanPostProcessors(beanFactory);
22 
23                 // Initialize message source for this context.
24                 initMessageSource();
25 
26                 // Initialize event multicaster for this context.
27                 initApplicationEventMulticaster();
28 
29                 // Initialize other special beans in specific context subclasses.
30                 onRefresh();
31 
32                 // Check for listener beans and register them.
33                 registerListeners();
34 
35                 // Instantiate all remaining (non-lazy-init) singletons.
36                 finishBeanFactoryInitialization(beanFactory);
37 
38                 // Last step: publish corresponding event.
39                 finishRefresh();
40             }
41 
42             catch (BeansException ex) {
43                 if (logger.isWarnEnabled()) {
44                     logger.warn("Exception encountered during context initialization - " +
45                             "cancelling refresh attempt: " + ex);
46                 }
47 
48                 // Destroy already created singletons to avoid dangling resources.
49                 destroyBeans();
50 
51                 // Reset 'active' flag.
52                 cancelRefresh(ex);
53 
54                 // Propagate exception to caller.
55                 throw ex;
56             }
57 
58             finally {
59                 // Reset common introspection caches in Spring's core, since we
60                 // might not ever need metadata for singleton beans anymore...
61                 resetCommonCaches();
62             }
63         }
64     }
View Code

當執行finishBeanFactoryInitialization(beanFactory)方法對非延遲初始化的單例bean進行初始化時,會執行到AbstractAutowireCapableBeanFactory類的doCreateBean方法

 1        // Allow post-processors to modify the merged bean definition.
 2         synchronized (mbd.postProcessingLock) {
 3             if (!mbd.postProcessed) {
 4                 try {
 5  applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
 6                 }
 7                 catch (Throwable ex) {
 8                     throw new BeanCreationException(mbd.getResourceDescription(), beanName,
 9                             "Post-processing of merged bean definition failed", ex);
10                 }
11                 mbd.postProcessed = true;
12             }
13         }

在這段代碼中會執行applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName),深入到這個applyMergedBeanDefinitionPostProcessors方法中,

1     protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
2         for (BeanPostProcessor bp : getBeanPostProcessors()) {
3             if (bp instanceof MergedBeanDefinitionPostProcessor) {
4                 MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
5                 bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
6             }
7         }
8     }

會發現這里調用的是AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition方法

在AbstractAutowireCapableBeanFactory#doCreateBean方法中有一段執行populateBean方法實現對屬性的注入

1        // Initialize the bean instance.
2         Object exposedObject = bean;
3         try {
4  populateBean(beanName, mbd, instanceWrapper);
5             exposedObject = initializeBean(beanName, exposedObject, mbd);
6         }
7         catch (Throwable ex) {
8             ...
9         }    

下面是AbstractAutowireCapableBeanFactory#populateBean方法中的一段代碼

 1     if (hasInstAwareBpps) {
 2             if (pvs == null) {
 3                 pvs = mbd.getPropertyValues();
 4             }
 5             for (BeanPostProcessor bp : getBeanPostProcessors()) {
 6                 if (bp instanceof InstantiationAwareBeanPostProcessor) {
 7                     InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
 8                     PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
 9                     if (pvsToUse == null) {
10                         if (filteredPds == null) {
11                             filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
12                         }
13                         pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
14                         if (pvsToUse == null) {
15                             return;
16                         }
17                     }
18                     pvs = pvsToUse;
19                 }
20             }
21         }
22         if (needsDepCheck) {
23             if (filteredPds == null) {
24                 filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
25             }
26             checkDependencies(beanName, mbd, filteredPds, pvs);
27         }

這段代碼中會遍歷所有注冊過的BeanPostProcessor接口實現類的實例,如果實例屬於InstantiationAwareBeanPostProcessor類型的,則執行實例類的postProcessPropertyValues方法。

1     @Deprecated
2     @Override
3     public PropertyValues postProcessPropertyValues(
4             PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
5 
6         return postProcessProperties(pvs, bean, beanName);
7     }
 1     @Override
 2     public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
 3         InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
 4         try {
 5  metadata.inject(bean, beanName, pvs);  6         }
 7         catch (BeanCreationException ex) {
 8             throw ex;
 9         }
10         catch (Throwable ex) {
11             throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
12         }
13         return pvs;
14     }

metadata.inject(bean, beanName, pvs)代碼的執行會進入如下inject方法中,在這里完成依賴的注入。

 1     public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
 2         Collection<InjectedElement> checkedElements = this.checkedElements;
 3         Collection<InjectedElement> elementsToIterate =
 4                 (checkedElements != null ? checkedElements : this.injectedElements);
 5         if (!elementsToIterate.isEmpty()) {
 6             for (InjectedElement element : elementsToIterate) {
 7                 if (logger.isTraceEnabled()) {
 8                     logger.trace("Processing injected element of bean '" + beanName + "': " + element);
 9                 }
10                 element.inject(target, beanName, pvs);
11             }
12         }
13     }

InjectedElement有兩個子類,分別是AutowiredFieldElement和AutowiredMethodElement。AutowiredFieldElement用於對標注在屬性上的注入,AutowiredMethodElement用於對標注在方法上的注入。LookupElement表示有關注釋字段或setter方法的通用注入信息的類,支持@Resource和相關注釋。

AutowiredFieldElement#inject和AutowiredMethodElement#inject中部分代碼,兩種方式的注入過程都差不多,根據需要注入的元素的描述信息,按類型或名稱查找需要的依賴值,如果依賴沒有實例化先實例化依賴,然后使用反射進行賦值。

1          if (value != null) {
2              ReflectionUtils.makeAccessible(field);
3              field.set(bean, value);
4          }
1             if (arguments != null) {
2                 try {
3                     ReflectionUtils.makeAccessible(method);
4                     method.invoke(bean, arguments);
5                 }
6                 catch (InvocationTargetException ex) {
7                     throw ex.getTargetException();
8                 }
9             }

 

@Autowired是spring框架提供的實現依賴注入的注解,主要支持在set方法,field,構造函數中完成bean注入,注入方式為通過類型查找bean,即byType的,如果存在多個同一類型的bean,則使用@Qualifier來指定注入哪個beanName的bean。

@Resource注解在功能和目的上,等效於Autowried+Qualifier注解。@Resource是基於bean的名字,即beanName,來從spring的IOC容器查找bean注入的,而@Autowried是基於類型byType來查找bean注入的。

Resource注解則順序不同,它有如下幾種可能的情況:

  1. Resource注解指定了name屬性和type屬性 策略:首先進行按名稱匹配策略: 匹配name屬性和bean的id,如果匹配,則判斷查找到的bean是否是type屬性指定的類型,如果是type屬性指定的類型,則匹配成功。如果不是type屬性指定的類型,則拋出異常,提示匹配失敗;如果name屬性跟bean的id不匹配,則拋出異常提示沒有bean的id匹配name屬性
  2. Resource注解指定了name屬性,未指定type屬性 策略:查找bean的id為name屬性的bean,查找到,不關心類型為什么,都是匹配成功;如果找不到name屬性指定的bean id,則匹配失敗,拋出異常
  3. Resource注解指定了type屬性,未指定name屬性 策略:首先進行按名稱匹配策略: 匹配屬性名和bean的id,如果匹配,則判斷查找到的bean是否是type屬性指定的類型,如果是type屬性指定的類型,則匹配成功。如果不是type屬性指定的類型,則拋出異常,提示匹配失敗;其次進行按類型匹配策略: 如果屬性名跟bean的id不匹配,則查找類型為type的bean,如果僅僅找到一個,自動裝配成功,其它情況失敗。
  4. Resource注解未指定type屬性和name屬性 策略:首先進行按屬性名匹配策略,匹配則注入成功;如果屬性名不匹配,則進行類型匹配策略,只有為一個類型匹配才成功,其他情況都失敗。

@Inject也是基於類型來查找bean注入的,如果需要指定名稱beanName,則可以結合使用@Named注解,而@Autowired是結合@Qualifier注解來指定名稱beanName。

 

spring依賴注入注解的實現原理:

  • 在spring框架內部實現當中,注解實現注入主要是通過bean后置處理器BeanPostProcessor接口的實現類來生效的。BeanPostProcessor后置處理器是在spring容器啟動時,創建bean對象實例后,馬上執行的,對bean對象實例進行加工處理。
  • @Autowired是通過BeanPostProcessor接口的實現類AutowiredAnnotationBeanPostProcessor來實現對bean對象對其他bean對象的依賴注入的;
  • @Resource和@Inject是通過BeanPostProcessor接口的實現類CommonAnnotationBeanPostProcessor來實現的,其中如名字所述,即公共注解CommonAnotation,CommonAnnotationBeanPostProcessor是spring中統一處理JDK中定義的注解的一個BeanPostProcessor。該類會處理的注解還包括@PostConstruct,@PreDestroy等。

 

相關文章參考:

Spring依賴注入:@Autowired,@Resource和@Inject區別與實現原理


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM