在Spring-IoC簡介中簡單介紹了什么是IoC以及一些相關(guān)的概念,這里將詳細介紹如何通過IoC容器提供的方法進行注入和如何進行注入配置。
依賴注入
IoC提供的依賴注入方法主要有屬性注入、構(gòu)造函數(shù)注入和工廠方法注入,這里依次介紹如何通過各個方法注入Bean。
屬性注入
屬性注入要求Bean提供一個默認的構(gòu)造器,并為需要注入的屬性提供對應(yīng)的setter方法。Spring會檢查Bean中是否有setter方法,但不會檢查是否有對應(yīng)的屬性成員。
Bean的代碼如下。
public class Car{
private int maxSpeed;
private String brand;
//如果要通過屬性注入則必須實現(xiàn)setter方法
public void setXX(<T> XX){
this.XX=XX;
}
}
我們這里通過XML文件對Bean進行設(shè)置,后面會詳細介紹所有的配置方式和配置內(nèi)容。
<bean id="car" class="Car">
<property name="maxSpeed"><value>200</value></property>
<property name="brand"><value>紅旗CA72</value></property>
</bean>
這樣設(shè)置完成后,當Spring啟動后,就會自動生成id為car,class為Car,屬性分別為200和紅旗CA72的Bean。
對于屬性的命名,要求變量的前兩個字母要么全部大寫,要么全部小寫,iCCard、iDCode是非法的。如果取非法的變量名,在試圖啟動Spring時,將會失敗。因此命名的建議是像QQ、MSN、ID等正常以大寫字母出現(xiàn)的專業(yè)術(shù)語,在Java中一律使用小寫形式。
構(gòu)造函數(shù)注入
當想要通過構(gòu)造函數(shù)注入時,需要在Bean中實現(xiàn)相應(yīng)的構(gòu)造函數(shù),并以想要注入的屬性為參數(shù)。
Bean的代碼如下。
public class Car{
private int maxSpeed;
private String brand;
public Car(int maxSpeed,String brand){...};
}
然后我們在XML文件中進行配置。
<bean id="car" class="Car">
<constructor-arg type="java.lang.String">
<value>紅旗</value>
</constructor-arg>
<constructor-arg type="double">
<value>200</value>
</constructor-arg>
</bean>
在通過構(gòu)造器注入時,配置文件中arg的順序與構(gòu)造器中的參數(shù)順序無關(guān),當只有一個構(gòu)造器的情況下上述配置文件才會生效。因此建議使用索引匹配入?yún)ⅲ?code><constructor-arg index="0" value="紅旗">。
當兩個構(gòu)造器僅有int和double的區(qū)別(如下),則此時還需要在配置文件中加上參數(shù)類型以作區(qū)分,即<constructor-arg index="0" type="int" value="200"/>。
public class Car{
private int maxSpeed;
private double averageSpeed;
private String brand;
public Car(int maxSpeed,String brand){...};
public Car(double averageSpeed,String brand){...};
}
如果構(gòu)造函數(shù)的參數(shù)的類型是可以辨別的(非基礎(chǔ)數(shù)據(jù)類型且入?yún)㈩愋透鳟悾?/strong>,則可以不提供類型和索引的信息。
構(gòu)造器注入可能會出現(xiàn)循環(huán)依賴的問題,即A類需要注入B類,B類需要注入A類,在這種情況下,使用屬性注入就可以解決了。
工廠方法注入
在使用框架的過程中很少會使用到工廠方法注入,以下僅舉2個例子。
- 非靜態(tài)工廠類
public class CarFactory{
public Car createHongQiCar(){}
}
<bean id="carFactory" class="CarFactory"/>
<bean id="car" factory-bean="carFactory" factory-method="createHongQiCar"/>
由于工廠類不是靜態(tài)的,因此需要先定義工廠類的bean。
- 靜態(tài)工廠類
public class CarFactory{
public static Car createHongQiCar(){}
}
<bean id="car" class="CarFactory" factory-method="createCar"/>
Bean的配置方式
配置Bean可以通過XML、注解、Java類和Groovy DSL這四種方式,下面來詳細介紹各個配置方法的細節(jié)。
基于XML的配置
對于基于XML的配置,Spring2.0以后采用Schema格式,使得配置文件更具擴展性,但文件頭聲明會復雜一些,下面是一個例子。
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"
>
<!-- 默認命名空間的配置 -->
<bean id="foo" class="com.smart.Foo"/>
<!-- aop命名空間的配置 -->
<aop:config>
<aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/>
</aop:config>
</beans>
在上面的代碼中定義了3個命名空間:
-
xmlns="http://www.springframework.org/schema/beans",默認命名空間:沒有空間名,用于Spring Bean的定義。 -
xsi標準命名空間:這個命名空間用于指定自定義命名空間的Schema樣式文件,是W3C定義的標準命名空間。 -
xmlns:aop="http://www.springframework.org/schema/aop",aop命名空間:這個命名空間是Spring配置aop的命名空間,即一種自定義的命名空間。
命名空間的定義分為兩個步驟:
- 第一步指定命名空間的名稱,如
xmlns:aop="http://www.springframework.org/schema/aop"。aop為命名空間的別名,而"http://www.springframework.org/schema/aop"是命名空間的全限定名,習慣上用文檔發(fā)布機構(gòu)的官方網(wǎng)站和相關(guān)網(wǎng)站目錄作為全限定名。如果命名空間的別名為空,則表示該命名空間為文檔默認的命名空間。 - 第二步指定命名空間的Schema文檔格式文件的位置,用空格或回車換行進行分隔。定義的語法如下
<命名空間1> <命名空間1Schema文件> <命名空間2> <命名空間2Schema文件>(注意之間的分隔符可以為空格或換行符)。Schema地址有2個用途:一是XML解析器可以獲取Schema文件并對文檔進行格式合法性驗證;二是IDE可以引用Schema文件對文檔編輯提供自動補全功能。
以上就是一個XML配置文件的結(jié)構(gòu)介紹,接下來將介紹如何對Bean進行定制。
-
字面值
對于基本類型或其封裝類,我們在XML中可以直接進行設(shè)置。對于字符串來說,如果其中包含了&、<、>、"、'這5種特殊字符,可以通過下面的![CDATA[]]進行包裝,使得XML解析器將其中的內(nèi)容當成純文本;或者可以通過轉(zhuǎn)義序列來表示&、<、>、"、&apos。<bean id="car" class="Car"> <property name="maxSpeed"> <value>200</value> </property> <property name="brand"> <value><![CDATA[紅旗&CA72]]></value> </property> </bean> -
引用其他bean
可以通過以下3種標簽對其他的bean進行引用。- bean:可以引用同一容器或父容器總的Bean,這是最常見的形式。
- local:只引用同一配置文件中的Bean。
- parent:只引用父容器中的Bean。
<!--通過bean--> <bean id="car" class="Car"/> <bean id="boss" class="Boss"> <property name="car"> <ref bean="car"/> </property> </bean> <!--通過local與通過bean的方式類似--> <!--通過parent--> <!--在父容器中定義car--> <bean id="car" class="Car"/> <!--在子容器中定義另一個car--> <bean id="car" class="Car"/> <bean id="boss" class="Boss"> <ref parent="car"><!--將引用父容器中的car--> </bean> -
內(nèi)部bean
我們也可以通過隱藏類那樣定義bean。<bean id="boss" class="Boss"> <property name="car"> <bean class="Car"> <property name="brand" value="紅旗"/> </bean> </property> </bean> -
null值
如果想要注入null值,需要顯示的將值設(shè)為<null/>,即<property name="brand"><value><null/></value></property>。 -
級聯(lián)屬性
如果想要通過這種方式直接對<bean id="boss" class="Boss"> <property name="car.brand" value="紅旗"/> </bean>boss中的car的屬性進行設(shè)置,則需要在Boss類中聲明一個初始化對象并實現(xiàn)getter()方法。public class Boss{ private Car car=new Car(); public Car getCar(){return car;} } -
集合類
- List
如果List的屬性類型可以通過字符串字面值進行配置,那么就可以使用這種方式,如<bean id="boss" class="Boss"> <property name="favorites"> <list> <value>看報</value> <value>滑雪</value> </list> </property> </bean>String[]、int[]等。
此外,List還可以通過<ref>注入容器中其他的Bean。- Set
<bean id="boss" class="Boss"> <property name="favorites"> <set> <value>看報</value> <value>滑雪</value> </set> </property> </bean>- Map
<bean id="boss" class="Boss"> <property name="jobs"> <map> <entry> <key><value>AM</value><key> <value>會見客戶</value> </entry> <entry> <key><value>PM</value><key> <value>開會</value> </entry> <!--如果鍵值對都是對象--> <entry> <key><ref bean="keyBean"/><key> <ref bean="valueBean"/> </entry> </map> </property> </bean> -
Properties
Properties屬性可以看成鍵值都是字符串的Map類型。<bean id="boss" class="Boss"> <property name="jobs"> <props> <prop key="mail">123@gmail.com</prop> </props> </property> </bean> -
強類型集合
Spring配置強類型和非強類型集合相同,會將值自動轉(zhuǎn)換為目標類型。public class Car{ private Map<Integer,String> passengers=new HashMap<Integer,String>(); } -
集合合并
<bean id="boss1" class="Boss"> <property name="favorites"> <set> <value>看報</value> </set> </property> </bean> <bean id="boss2" class="Boss" parent="boss1"> <property name="favorites"> <set merge="true"> <!--合并父類中的同名set--> <value>看報</value> </set> </property> </bean> -
配置集合類型的Bean
如果想要配置集合類型的Bean,而不是屬性類型的集合,可以通過在Spring配置文件中引入util命名空間的聲明,然后進行配置。<beans xmlns:util="http://www.springframework.org/schema/util"> xsi:shcemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd" <util:list id="favorite" list-class="java.util.LinkedList"> <value>喝茶</value> <value>看報</value> </util:list> </beans>
簡化XML配置方式
- 字面值屬性
| 類型 | 簡化前 | 簡化后 |
|---|---|---|
| 字面值屬性 | <property name="maxSpeed"><value>200</value></property> | <property name="maxSpeed" value="200"/> |
| 構(gòu)造函數(shù)參數(shù) | <constructor-arg index="0"><value>200</value></constructor-arg> | <constructor-arg index="0" value="200"/> |
| 集合元素 | <map> <entry> <key><value>AM</value></key> <value>見客戶</value> </entry> </map> |
<map> <entry key="AM" value="見客戶"/> </map> |
- 引用對象屬性
| 類型 | 簡化前 | 簡化后 |
|---|---|---|
| 字面屬性值 | <property name="car"> <ref-bean="car"/></property> | <property name="car" ref="car"> |
| 構(gòu)造函數(shù) | <constructor-arg> <ref-bean="car"></property> | <constroctor-arg ref="car"/> |
| 集合元素 | <entry> <key><ref bean="keyBean"/></key> <ref bean="valueBean"> </entry> |
<entry key-ref="keyBean" value-ref="valueBean"/> |
<ref>的簡化形式對應(yīng)于<ref bean="xxx">,而<ref local>和<ref parent>沒有對應(yīng)的簡化形式。
- 使用p命名空間
<beans xmlns:p="http://www.springframework.org/schema/p"><!--聲明p命名空間-->
<bean id="car" class="Car"
p:brand="紅旗"
p:maxSpeed="200"/>
<bean id="boss" class="Boss"
p:car-ref="car"/>
</beans>
基于注解的配置
采用基于注解的配置文件,則Bean定義信息通過在Bean實現(xiàn)類上標注注解實現(xiàn)。
//這里定義了一個Dao的Bean
/*
與@Component功能相似的還有:
@Repository:用于對DAO實現(xiàn)類進行標注
@Service:用于對Service實現(xiàn)類進行標注
@Controller:用于對Controller實現(xiàn)類進行標注
*/
@Component("carDao")
public class CarDao{
//..
}
對于通過注解定義的bean需要在Spring配置文件中進行額外設(shè)置。
<beans
xmlns:context="http://www.springframework.org/schema/context"
xsi:"http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd"
>
<!--掃描類包以應(yīng)用注解的bean,可以通過resource-pattern進行過濾 -->
<!-- 其余過濾的方法還有include-filter和exclude-filter -->
<context:component-scan base-package="package" resource-pattern="./*.class">
</beans>
對Bean完成配置后,還需要通過注解進行自動注入
@Service
public class LoginService{
//注入LogDao的bean
//對required=false設(shè)置,當Spring啟動時如果未找到對應(yīng)的bean則不會報錯
//通過Qualifier可以指定對應(yīng)bean的名稱
@Autowired(required=false)
@Qualifier("logDao")
private LogDao logDao;
//對類方法進行標注
@Autowired
@Qualifier("logDao")
public void setLogDao(LogDao logDao){}
//或者通過這種形式
public void setLogDao(@Qualifier("logDao")LogDao logDao){}
//如果對類中集合類的變量或方法入?yún)⑦M行標注,那么Spring會將容器中類型匹配的所有Bean都注入
@Autowired
private List<Plugin> plugins;
//Spring如果發(fā)現(xiàn)變量是一個list和一個map,會將容器中匹配集合元素類型的所有bean都注入
//這里將會把plugins的bean注入map集合,key是bean的名字,value是所有實現(xiàn)了plugin的bean。
@Autowired
private Map<String,Plugin> pluginMaps;
//如果Plugin有多個實現(xiàn)類,那么可以在不同的實現(xiàn)類前用@Order(value=1)對注入順序進行設(shè)置,越小越先加載
}
基于Java類的配置、基于Groovy DSL的配置
一般來說,使用XML和標注的方式就能解決幾乎所有的任務(wù)。建議使用XML配置DataSource、SessionFactory等資源bean,在XML中利用aop、context命名空間進行相關(guān)主題的配置,其余所有項目中開發(fā)的Bean都通過基于注解配置的方式進行配置。
Bean基本配置
Bean的命名
Bean的id屬性命名與Java變量的命名要求相同,而name屬性命名沒有任何字符上的限制??梢酝ㄟ^不設(shè)置id屬性命名來實例化匿名Bean。
<bean class="Car"/>
<bean class="Car"/>
<bean class="Car"/>
這樣就實例化了3個匿名Bean,第一個Bean通過getBean("Car")獲得,第二個通過getBean("Car#1")獲得。
依賴注入
Spring支持屬性注入和構(gòu)造函數(shù)注入,除此之外還支持工廠法注入方式。