Spring之Bean的註入
Bean的配置中介紹的是Bean聲明問題,在哪聲明怎麽聲明的問題。Bean的註入是怎麽實例化,怎麽註入的問題。Bean註入的方式有兩種,一種是在XML中配置,另一種則是使用註解的方式註入。
一、XML方式註入
XML方式註入一般有三種方式:屬性註入、構造函數註入和工廠方法註入。
一、屬性註入
在傳統的對象實例化時可以通過new class(),然後通過setXXX()方法設置對象的屬性值或依賴對象,屬性註入也是采用這種方式,只是Spring框架會在內部完成這些操作,它會先調用Bean的默認構造函數實例化Bean對象,然後通過反射的方式調用Setter方法註入屬性值。它會使用默認的構造函數(無參數構造函數),只需為註入的屬性設置set方法,可選擇性和靈活性比較高,所以也是比較常用的一種註入方式。這裏示例還是在IOC章節使用人和空氣的基礎上稍作修改來演示。IAir接口和CleanAir、DirtyAir類不變,這裏就不貼了。
1.新建XMLInstance類
package com.demo.model; public class XMLInstance { private String name; public void setName(String name) { this.name = name; } private IAir air; public void setAir(IAir air) { this.air = air; } public void Breath() { System.out.println(View Code"Name:"+this.name+";Air:"+this.air.toString()); } public XMLInstance(String name, IAir air) { super(); this.name = name; this.air = air; } public XMLInstance() { } public void DestoryMethod() { System.out.println("DestoryMethod"); }public void InitMethod() { System.out.println("InitMethod"); } }
在XMLInstance類中並未聲明構造函數,對於air屬性只設置了set方法,get方法也沒設置。
<bean id="CleanAir" class="com.demo.model.CleanAir"> <qualifier value="cleanair"/> </bean> <bean id="xmlinstance" class="com.demo.model.XMLInstance"> <property name="air" ref="CleanAir"></property> <property name="name" value="abc"></property> </bean>
Xml中使用property類配置屬性,name是屬性名,value用來設置基本數據類型的屬性值。Spring配置文件中bean之間可以相互引用,引用時可以用<ref>標簽配置bean的id屬性使用。<ref>可以用在<property>屬性中,也可以用在<construct-arg>構造函數的參數值,還可以用在其他地方,通過引用能減少bean的聲明。
二、構造函數註入
在屬性註入時先使用默認的構造函數(無參數構造函數)實例化,然後通過set方法註入屬性,在傳統實例化對象時可以自定義構造函數進行實例化,構造函數註入就是通過自定義構造函數來進行對象的實例化。這裏在XMLInstance類的基礎上增加了一個構造函數,第一個參數是String類型的name,第二個參數是IAir類型的air。
public XMLInstance(String name, IAir air) { super(); this.name = name; this.air = air; }
Xml中使用使用<construect-arg>來設置構造函數的參數,index屬性設置參數的順序,參數順序應該與構造函數的一致,ref設置引用bean的id,value設置構造函數參數的值。
<bean id="xmlcontructinstance" class="com.demo.model.XMLInstance"> <constructor-arg index="1" ref="CleanAir"></constructor-arg> <constructor-arg index="0" value="abc"></constructor-arg> </bean>
三、工廠方法註入
工廠方法註入參考的是工廠設計模式,通過在工廠類中實現對象的實例化。工廠類負責創建一個或多個目標類實例,工廠類方法一般以接口或抽象類變量的形式返回目標類實例,工廠類對外屏蔽了目標類的實例化步驟,調用者甚至不用知道具體的目標類是什麽。工廠方法也分靜態工廠方法和非靜態工廠方法,靜態工廠方式不用實例化工廠類,直接通過類名調用,非靜態工廠方法需要先實例化工廠類,然後通過工廠類對象調用獲取對象。這裏創建了一個工廠類XMLFactory,在類中定義了一個靜態方法,和一個實例方法用來實例化bean對象。
package com.demo.model; public class XMLFactory { public XMLInstance CreateInstance() { return new XMLInstance("instance",new CleanAir()); } public static XMLInstance CreateStaticInstance() { return new XMLInstance("static instance",new CleanAir()); } }
1.靜態工廠方法
只需設置工廠方法對應的類,以及對應的工廠方法。
<bean id="xmlfactorystaticinstance" class="com.demo.model.XMLFactory" factory-method="CreateStaticInstance"></bean>
2.實例工廠方法
需要先實例化工廠類,再通過工廠類對象調用實例方法獲取bean對象。
<bean id="xmlfactoryinstance" factory-bean="xmlfactory" factory-method="CreateInstance" destroy-method="DestoryMethod" init-method="InitMethod"></bean>
四、常見數據類型註入
(1)List屬性註入
使用<list>配置java.util.List類型的屬性。List屬性中元素可以是任何數據類型的值,如果是Java對象可以使用ref指定,或使用<bean>定義新實例。如果是基礎數據類型可直接用字符串。<list>中的元素會按配置的先後順序排序。
<property name="lists">
<list>
<value>1</value>
<ref bean="CleanAir" />
<bean class="com.demo.model.CleanAir"/>
</list>
</property>
(2)Set屬性註入
使用<set>配置java.util.Set類型的屬性。Set屬性中元素可以是任何數據類型的值,如果是Java對象可以使用ref指定,或使用<bean>定義新實例。如果是基礎數據類型可直接用字符串。<set>中的元素沒有先後順序。
<property name="sets"> <set> <value>1</value> <ref bean="CleanAir" /> <bean class="com.demo.model.CleanAir"/> </set> </property>
(3)Map屬性註入
使用<map>配置java.util.Map類型的屬性。<entry>配置Map裏的元素,Key指定索引,value指定值。如果是Java對象可以使用ref指定,或使用<bean>定義新實例。
<property name="maps"> <map> <entry key="key1" value="1"></entry> <entry key="key2" value-ref="CleanAir"></entry> <entry key="key3" > <bean class="com.demo.model.CleanAir"/> </entry> </map> </property>
(4)Properties屬性註入
使用<props>配置java.util.Properties類型的屬性。<props>配置一個Properties對象,<prop>配置一條屬性,屬性Key配置索引。
<property name="pros"> <props> <prop key="prokey1">prokeyA</prop> <prop key="prokey2">prokeyB</prop> </props> </property>
(5)自定義屬性編輯器
對於有一些屬性是沒法註入的,此時就需要自定義,比如日期類型。可以通過繼承PropertyEditorSupport的類,重寫setAsText方法來實現註入。這裏定義了CustomerProperty繼承PropertyEditorSupport,重寫了setAsText方法,並將該bean配置到xml中。
package com.demo.model; import java.beans.PropertyEditorSupport; import java.text.ParseException; import java.text.SimpleDateFormat; public class CustomerProperty extends PropertyEditorSupport { private String format="yyyy-MM-dd"; public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } @Override public void setAsText(String text) throws IllegalArgumentException { SimpleDateFormat sdf=new SimpleDateFormat(format); //super.setAsText(text); try { //轉換對象,能過setValue方法重新賦值 this.setValue(sdf.parse(text)); } catch (ParseException e) { e.printStackTrace(); } } }
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.util.Date" value="com.demo.model.CustomerProperty"/> </map> </property> </bean>
配置之後就可以註入Date類型的屬性了。
<property name="date" value="2018-8-20"/>
這裏新建了XmlCollectionsDemo類,配置了上面的幾個類型的屬性來演示。
package com.demo.model; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class XmlCollectionsDemo { private List<Object> list; private Properties pros; private Set<Object> sets; private Map<String,Object> maps; private Date date; public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public List<Object> list() { return list; } public void setLists(List<Object> list) { this.list = list; } public Properties getPros() { return pros; } public void setPros(Properties pros) { this.pros = pros; } public Set<Object> getSets() { return sets; } public void setSets(Set<Object> sets) { this.sets = sets; } public Map<String, Object> getMaps() { return maps; } public void setMaps(Map<String, Object> maps) { this.maps = maps; } public static void main( String[] args ) throws Exception { ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"ApplicationContext.xml"}); BeanFactory factory=context; XmlCollectionsDemo annontationInstance=(XmlCollectionsDemo)factory.getBean("xmlCollectionsDemo"); System.out.println(annontationInstance.list); System.out.println(annontationInstance.pros); System.out.println(annontationInstance.sets); System.out.println(annontationInstance.maps); System.out.println(annontationInstance.date); } }View Code
<bean id="xmlCollectionsDemo" class="com.demo.model.XmlCollectionsDemo"> <property name="lists"> <list> <value>1</value> <ref bean="CleanAir" /> <bean class="com.demo.model.CleanAir"/> </list> </property> <property name="sets"> <set> <value>1</value> <ref bean="CleanAir" /> <bean class="com.demo.model.CleanAir"/> </set> </property> <property name="maps"> <map> <entry key="key1" value="1"></entry> <entry key="key2" value-ref="CleanAir"></entry> <entry key="key3" > <bean class="com.demo.model.CleanAir"/> </entry> </map> </property> <property name="pros"> <props> <prop key="prokey1">prokeyA</prop> <prop key="prokey2">prokeyB</prop> </props> </property> <property name="date" value="2018-8-20"/> </bean>View Code
通過運行main方法,打印出屬性值。
[1, CleanAir, CleanAir]
{prokey2=prokeyB, prokey1=prokeyA}
[1, CleanAir, CleanAir]
{key1=1, key2=CleanAir, key3=CleanAir}
Mon Aug 20 00:00:00 CST 2018
五、初始化函數、銷毀函數
通過上面3種註入方式的學習也對通過xml對bean實例化有的了解,有的對象在實例化之後還需要執行某些初始化代碼,但這些初始化代碼還不能寫在構造函數中,此時可以將初始化代碼寫到某個方法中,將init-method屬性值設置為該方法,Spring會強制執行該方法進行初始化。而又的對象在使用完畢之後需要釋放,可以使用destroy-method來進行銷毀。
public void DestoryMethod() { System.out.println("DestoryMethod"); } public void InitMethod() { System.out.println("InitMethod"); }
這裏先在XMLInstance類中增加了上面兩個方法來模擬銷毀和初始化方法。然後在xml配置bean時就可以設置destroy-method、init-method屬性的值對應兩個方法的方法名。註解中@PostConstruct對應init-method,@PreDestory對應destroy-method。
<bean id="xmlfactoryinstance" factory-bean="xmlfactory" factory-method="CreateInstance" destroy-method="DestoryMethod" init-method="InitMethod"></bean>
二、註解註入方式
一、常用註解介紹
學習完XML註入之後再學習註解方式註入就容易的多,註解方式註入主要涉及到@Autowired,@Resource,@Required,@Qualifier,@Value這幾個註解。在第2章節的2.2.4IOC實例中定義Person時就使用過@Autowired、@Qualifier。下面來了解下它們具體用法。
@Autowired:默認是按類型匹配註入bean,它可以對類成員變量、方法及構造函數進行標註,完成自動裝配的工作。在使用@Autowired時,首先在容器中查詢對應類型的bean,如果查詢結果剛好為一個,就將該bean裝配給@Autowired指定的數據,如果查詢的結果不止一個,那麽@Autowired會根據名稱來查找。如果查詢的結果為空,那麽會拋出異常。解決方法時,使用required=false。
@Required:適用於bean屬性setter方法,並表示受影響的bean屬性必須在XML配置文件在配置時進行填充。否則,容器會拋出一個BeanInitializationException異常。
@Qualifier:@Autowired默認是單實例的,但是在面向接口編程中,如果把一個屬性設置為接口類型,一個接口可能有多個實現,那到底是註入哪一個呢?為了解決這個問題,就有了@Qualifier。
@Value:在xml配置屬性時可以通過property的value設置默認值,@Value也可以為屬性設置默認值。
@Resource:默認按名稱匹配註入bean。要求提供一個bean名稱的屬性,如果屬性為空,則自動采用標註處的變量名或方法名作為bean的名稱。如果我們沒有在使用@Resource時指定bean的名字,同時Spring容器中又沒有該名字的bean,這時候@Resource就會退化為@Autowired即按照類型註入。
package com.demo.model; import javax.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class AnnontationInstance { @Value("abc") private String name; public void setName(String name) { this.name = name; } //@Resource與@Autowired兩者選其一 // @Autowired // @Qualifier(value="cleanair") private IAir air; @Resource(name="CleanAir") public void setAir(IAir air) { this.air = air; } public void Breath() { System.out.println("Name:"+this.name+";Air:"+this.air.toString()); } }
上面代碼使用@Value註解為name設置了默認值,使用@Resources設置bean的名稱為IAir屬性註入bean,也可以使用@Autowired+@Qualifier為IAir註入bean。
二、開啟註解
上面配置完註解之後,還要告訴Spring開啟註解,這樣@Autowired、@Resources這些註解才起作用。開啟有兩種比較簡單的方式。
1.在xml配置文件中使用context:annotation-config
<context:annotation-config />
2.在xml配置文件中使用context:component-scan
<context:component-scan base-package="com.demo.model"/>
Spring之Bean的註入