javascript
【Core Spring】二、装配beans
在Spring中,對象不負責尋找和創建它們需要的其他對象。創建兩個應用對象之間關聯的動作是依賴注入的核心功能,通常稱為裝配。
創建beans和構建它們之間的關系是Spring的責任,但是告訴Spring哪些bean需要被創建并且怎樣將它們裝裝配到一起是開發者的責任。Spring提供了三種基本的裝配機制。
-
-
- 顯式地通過XML配置
- ? ?顯式地通過Java配置
- ? ?隱式地發現bean并且自動裝配
-
?
自動裝配beans
Spring從兩個角度解決自動裝配
-
-
- ? ?Component scanning----Spring自動發現beans并在容器中創建
- ? ?Autowiring-----Spring自動構建bean的依賴
-
組件掃描
組件掃描不是默認開啟項,需要顯式配置以便Spring尋找帶有@Component注解的類并創建它們。@Component注解可以在它的屬性中標注bean的ID。例如,@Component("id"),如果沒有顯式標注ID,那么Spring會將這個bean的ID設為類名并將首字母小寫。基于Java配置如下:
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class CDPlayerConfig { }除此之外,可以用Java Dependency Injection specification(JSR-330)中的注解@Named為bean提供ID。Spring支持將@Named替換@Component。
如果沒有更多配置@ComponentScan將會默認掃描與配置類所在包相同的類。但是,應該顯式地設置掃描的base package,因為那樣你可以將所有的配置代碼歸置到一個包中,區別于應用代碼。例如這樣@ComponentScan("basepackagename")、或者@ComponentScan(basePackages="soundsystem"),多個包的情況,@ComponentScan(basePackages={"soundsystem", "video"})。用上述String形式配置base package時,并不是類型安全的,如果重構包名,指定的base package將會出錯,因此@ComponentScan可以通過base package中的類或者接口定義base package,如:@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
基于XML配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="soundsystem" /> </beans>自動裝配
如果應用程序中所有的對象都是獨立的,沒有依賴的,那么組建掃描就能完成所需功能。但是如果對象包含對其他對象的依賴,那么自動裝配就能派上用場了。
@Autowired注解可以用在構造函數、屬性的setter方法甚至任何方法上。無論構造函數、setter方法或其他任何方法,Spring將會嘗試構建參數的依賴。僅當有且僅有一個bean匹配,才會將這個bean裝配。如果沒有bean匹配,Spring會在容器創建時拋出一個異常。為了避免這個異常你可以設置@Autowired的required屬性為false,@Autowired(required=false)。當required屬性為false時,Spring將會進行自動裝配,但是如果沒有bean匹配,會使需要注入依賴關系的bean未裝配。你應該十分謹慎地設置required屬性為false。bean的屬性未被裝配,有可能發生空指針異常。如果有多個bean匹配,Spring將會拋出異常表示出現歧義現象。
同樣的,可以用Java Dependency Injection specification中的注解@Inject代替@Autowired。
通過Java裝配bean
盡管通過自動裝配的方式構造依賴很方便,但是有時我們必須顯式地配置Spring。比如,需要從第三方類庫中裝配一些組件進入你自己的應用。因為你無法獲取源碼,所以沒有機會在類上標記@Component和@Autowired注解。
Java方式配置優于XML方式的配置,它更強大,類型安全,方便重構。
創建一個JavaConfig類的關鍵在于用@Configuration注解標記它。通過編寫一個創建所需類型實例的方法并且用@Bean標記它的方式在JavaConfig中聲明一個bean。
@Bean public CompactDisc sgtPeppers() { return new SgtPeppers(); }默認地,bean的ID會被設置為同@Bean注解標識的方法名稱一樣。也可以指定ID,@Bean(name="lonelyHeartsClubBand")。
如果一個bean需要依賴其他對象,最簡單的裝配方法是引用被依賴對象的方法,如下:
@Bean public CDPlayer cdPlayer() { return new CDPlayer(sgtPeppers()); }cdPlayer方法與sgtPeppers方法有著微妙的不同,CDPlayer對象是通過調用接受CompactDisc對象作為參數的構造方法來創建的,而不是通過默認構造方法。貌似CompactDisc對象是通過調用sgtPeppers來構造的,但并不是這樣。因為sgtPeppers()方法被注解了@Bean,Spring將會攔截任何對它的調用,以確保由sgtPeppers()方法生成的對象已經被返回了,而不是允許sgtPeppers()又一次被調用。例如:
@Bean public CDPlayer cdPlayer() { return new CDPlayer(sgtPeppers()); } @Bean public CDPlayer anotherCDPlayer() { return new CDPlayer(sgtPeppers()); }如果對sgtPeppers()方法的調用與其他Java方法一樣,那么每一個CDPlayer將會擁有自己的SgtPeppers實例。默認情況下,Spring的beans都是單例的。所以Spring攔截對sgtPeppers()方法的調用以確保返回的是由Spring自己調用sgtPeppers()方法生成的CompactDisc Bean。因此,兩個CDPlayer Bean擁有相同的SgtPeppers實例。
這樣做法很迷惑,下面是一種更簡單和容易理解的方法。
@Bean public CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc); }cdPlayer方法接受一個CompactDisc對象作為參數。當Spring調用cdPlayer方法生成CDPlayer Bean時,將會自動裝配一個CompactDisc對象進入cdPlayer方法。
如果需要通過setter方法的方式實現DI,如下:
@Bean public CDPlayer cdPlayer(CompactDisc compactDisc) { CDPlayer cdPlayer = new CDPlayer(compactDisc); cdPlayer.setCompactDisc(compactDisc); return cdPlayer; }?
通過XML裝配bean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context"> <!-- configuration details go here --> </beans>相當于@Configuration
<bean class="soundsystem.SgtPeppers" />相當于@Bean,如果沒有指定ID會被默認命名為全稱限定名,此例中為soundsystem.SgtPeppers#0。#0是用來區分類型相同bean的編號。最好指定ID
<bean id="compactDisc" class="soundsystem.SgtPeppers" />首先值得注意的是,不像在JavaConfig中,你直接負責創建一個SgtPeppers實例。當Spring看見<bean>元素,它會通過默認構造函數幫你創建一個SgtPeppers bean。用XML配置Bean創建更加被動。
?
另外一件值得注意的是,bean的類型通過class屬性的字符串設置。但是誰能保證class屬性指向一個真的類?Spring的XML配置無法在編譯期驗證指向的Java類型。甚至于,如果你重命名了這個類,它將指向一個不存在的類。
在SpringXML配置中,只有一種方法聲明bean,通過<bean>元素。但是當通過XML聲明DI,將會有幾種方式和風格。具體到構造器注入,主要有兩種選擇:
-
-
- <constructor-arg>元素
- 使用Spring3.0中引入的c-namespace
-
兩者之間的差異是非常復雜的。如你所見,<constructor-arg>元素通常比c-namespace更冗長,在XML中也更難閱讀。另一方面,<constructor-arg>元素可以做一些c-namespace做不到的。在構造器注入中,我們將兩者作為并列選項。
<bean id="cdPlayer" class="soundsystem.CDPlayer"> <constructor-arg ref="compactDisc" /> </bean>當Spring容器見到<bean>元素,它會創建一個CDPlayer實例。<constructor-arg>元素告訴它傳遞一個ID是compactDisc的bean的引用給CDPlayer的構造器。當然也可以使用Spring的c-namespace。c-namespace是Spring3.0中引入的一種更簡潔的XML中構造器注入方式。如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc" /> </beans>c代表命名空間,cd代表構造器參數名稱,-ref是一種命名約定,引入一個名為compactDisc的bean。但是通過參數名稱引用bean會產生問題,指向參數位置會更好。
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_0-ref="compactDisc" />這個比上一個XML看起來還古怪,由于XML不允許數字作為屬性的第一個字符,所以用了_作為前綴。如果只有一個參數,可以根本不制定參數
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_-ref="compactDisc" />通過構造器注入基本類型如下
<bean id="compactDisc" class="soundsystem.BlankDisc"> <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" /> <constructor-arg value="The Beatles" /> </bean>c-namespace方式
<bean id="compactDisc" class="soundsystem.BlankDisc" c:_title="Sgt. Pepper's Lonely Hearts Club Band" c:_artist="The Beatles" /> 或 <bean id="compactDisc" class="soundsystem.BlankDisc" c:_0="Sgt. Pepper's Lonely Hearts Club Band" c:_1="The Beatles" /><constructor-arg>元素可以做但是c-namespace方式無法實現的是注入容器
<bean id="compactDisc" class="soundsystem.BlankDisc"> <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" /> <constructor-arg value="The Beatles" /> <constructor-arg> <list> <value>Sgt. Pepper's Lonely Hearts Club Band</value> <value>With a Little Help from My Friends</value> <value>Lucy in the Sky with Diamonds</value> <value>Getting Better</value> <value>Fixing a Hole</value> <!-- ...other tracks omitted for brevity... --> </list> </constructor-arg> </bean> 或 <constructor-arg> <list> <ref bean="sgtPeppers" /> <ref bean="whiteAlbum" /> <ref bean="hardDaysNight" /> <ref bean="revolver" /> ... </list> </constructor-arg>setter方法注入
<bean id="cdPlayer" class="soundsystem.CDPlayer"> <property name="compactDisc" ref="compactDisc" /> </bean>同樣的,Spring提供p-namespace代替<property>元素。需要引入如下命名空間。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> ... </bean>用p-namespace裝配屬性,具體含義同c-namespace相同。
<bean id="cdPlayer" class="soundsystem.CDPlayer" p:compactDisc-ref="compactDisc" />p-namespace無法裝配容器。
轉載于:https://www.cnblogs.com/m-evan/p/5356077.html
總結
以上是生活随笔為你收集整理的【Core Spring】二、装配beans的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络流再结
- 下一篇: javascript:void(0)和j