Spring9:Autowire(自動裝配)機制


為什么Spring要支持Autowire(自動裝配)

先寫幾個類,首先定義一個Animal接口表示動物:

 1 public interface Animal {
 2 
 3     public void eat();
 4     
 5 }

寫一個Animal接口的實現Tiger類:

 1 public class Tiger implements Animal {
 2 
 3     @Override
 4     public void eat() {
 5         System.out.println("Tiger.eat()");
 6     }
 7     
 8     @Override
 9     public String toString() {
10         return "I'm a tiger";
11     }
12 }

寫一個動物園類Zoo,持有Animal接口,表示動物園中有動物:

 1 public class Zoo {
 2     
 3     private Animal animal;
 4     
 5     public Animal getAnimal() {
 6         return animal;
 7     }
 8 
 9     public void setAnimal(Animal animal) {
10         this.animal = animal;
11     }
12 
13     @Override
14     public String toString() {
15         if (animal == null) {
16             return null;
17         }
18         return animal.toString();
19     }
20     
21 }

配置一下spring文件,由於這個功能研究的是Autowire,因此我命名為autowire.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="tiger" class="org.xrq.action.by.Tiger" />
 8     
 9     <bean id="zoo" class="org.xrq.action.by.Zoo">
10         <property name="animal" ref="tiger" />
11     </bean>
12         
13 </beans>

Spring引入Autowire(自動裝配)機制就是為了解決<bean>標簽下<property>標簽過多的問題,<property>標簽過多會引發兩個問題:

  • 如果一個Bean中要注入的對象過多,比如十幾二十個(這是很正常的),那將導致Spring配置文件非常冗長,可讀性與維護性差
  • 如果一個Bean中要注入的對象過多,配置麻煩且一不小心就容易出錯

因此,為了解決使用<property>標簽注入對象過多的問題,Spring引入自動裝配機制,簡化開發者配置難度,降低xml文件配置大小。

 

使用Autowire去除<property>標簽

下面來看一下使用Autowire去除<property>,autowire有兩處點:

  • 可以配置在<beans>根標簽下,表示對全局<bean>起作用,屬性名為default-autowire
  • 可以配置在<bean>標簽下,表示對當前<bean>起作用,屬性名為autowire

通常都是在<beans>根標簽下配置自動裝配比較多,default-autowire有四種取值:

  • no:默認,即不進行自動裝配,每一個對象的注入比如依賴一個<property>標簽
  • byName:按照beanName進行自動裝配,使用setter注入
  • byType:按照bean類型進行自動裝配,使用setter注入
  • constructor:與byType差不多,不過最終屬性通過構造函數進行注入

這里研究的是去除<property>標簽,因此第一種不管;constructor裝配不太常用,因此這里也不管,重點看最常用的byName與byType,至於具體使用哪種根據自己的業務特點進行相應的設置。

首先看一下byName,byName意為在spring配置文件中查詢beanName與屬性名一致的bean並進行裝配,若類型不匹配則報錯,autowire.xml如果使用byName進行屬性裝配,那么將改成以下的形式:

 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     default-autowire="byName">
 7     
 8     <bean id="animal" class="org.xrq.action.by.Tiger" />
 9     
10     <bean id="zoo" class="org.xrq.action.by.Zoo" />
11         
12 </beans>

看到Zoo中有一個名為animal的屬性,我將Tiger這個bean也命名為animal,由於Tiger是Animal接口的實現類,因此Spring可以找到beanName為animal的bean並自動裝配到Zoo的animal屬性中,這就是byName的自動裝配形式。

接着看一下byType的自動裝配形式,byType意為在spring配置文件中查詢與屬性類型一致的bean並進行裝配,若有多個相同類型則報錯(下一部分講),autowire.xml如果使用byType進行屬性裝配,那么將改成以下的形式:

 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     default-autowire="byType">
 7     
 8     <bean id="tiger" class="org.xrq.action.by.Tiger" />
 9     
10     <bean id="zoo" class="org.xrq.action.by.Zoo" />
11         
12 </beans>

將Tiger命名為tiger(將bean命名為類名首字母小寫也比較符合規范),由於Tiger是Animal接口的實現類,因此Spring找到了Tiger並自動裝配到Zoo的animal屬性中,這就是byType的自動裝配形式。

 

byType裝配出現多個相同類型的bean及解決方案

前面演示了,byType的裝配方式是在Spring配置文件中尋找屬性類型與bean類型一致的bean,那么有一個問題,就是如果屬性類型在Spring配置文件中有多個相同類型的bean會出現什么樣的情況?為了探究一下這個問題,先定義另外一個Animal接口的實現類,叫做lion:

 1 public class Lion implements Animal {
 2 
 3     @Override
 4     public void eat() {
 5         System.out.println("Lion.eat()");
 6     }
 7 
 8     @Override
 9     public String toString() {
10         return "I'm a lion";
11     }
12 }

接着,在Spring配置文件中定義一下Lion這個類:

 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     default-autowire="byType">
 7     
 8     <bean id="tiger" class="org.xrq.action.by.Tiger" />
 9     <bean id="lion" class="org.xrq.action.by.Lion" />
10     
11     <bean id="zoo" class="org.xrq.action.by.Zoo" />
12         
13 </beans>

運行一個測試類,結果為:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'zoo' defined in class path resource [spring/autowire.xml]: 
Unsatisfied dependency expressed through bean
property 'animal': : No unique bean of type [org.xrq.action.by.Animal] is defined: expected single matching bean but
found 2: [tiger, lion]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.xrq.action.by.Animal] is
defined: expected single matching bean but found 2: [tiger, lion] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1166) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1058) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:516) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:455) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)

意思很明顯:想要通過byType方式為animal進行裝配卻找到了兩個符合要求的bean,分別為tiger與lion,這導致了沒有唯一的bean可以對animal進行裝配。

這個問題有兩種解決方案,假如現在我要裝配的是lion這個bean,第一種解決方案是將不需要進行自動裝配的bean進行排除,對不需要進行自動裝配的bean設置屬性autowire-candidate="false"即可

 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     default-autowire="byType">
 7     
 8     <bean id="tiger" class="org.xrq.action.by.Tiger" autowire-candidate="false" />
 9     <bean id="lion" class="org.xrq.action.by.Lion" />
10     
11     <bean id="zoo" class="org.xrq.action.by.Zoo" />
12         
13 </beans>

candidate顧名思義,即候選人的意思,autowire-candidate="false"即這個bean我不想讓它作為自動裝配的候選者,既然tiger不是自動裝配的候選者,那么animal類型在Spring容器中能自動裝配的也就只有一個lion了,Spring自動裝配lion,不會有問題。

第一種思路是排除那些不需要作為自動裝配候選者的bean,第二種思路就從相反邏輯出發,設置當發現有多個候選者的時候優先使用其中的哪個候選者,對要作為自動裝配候選者的bean設置primary="true"即可

 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     default-autowire="byType">
 7     
 8     <bean id="tiger" class="org.xrq.action.by.Tiger" />
 9     <bean id="lion" class="org.xrq.action.by.Lion" primary="true" />
10     
11     <bean id="zoo" class="org.xrq.action.by.Zoo" />
12         
13 </beans>

這種方式同樣也可以將lion裝配到animal屬性中而不會報錯。


免責聲明!

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



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