測(cè)試同學(xué)上手Spring 之DI深入解析
前面已經(jīng)介紹了如何上手Spirng編碼以及IOC的核心概念,今天給大家講解Spring的另一個(gè)重點(diǎn)——DI。
Spring核心模塊
DI概念
IoC 其實(shí)有兩種方式,一種就是 DI(Dependency Injection) ,而另一種是 DL(Dependency Lookup)即依賴(lài)查找。前者是當(dāng)前組件被動(dòng)接受IoC容器注入的依賴(lài)組件,而后者則是組件主動(dòng)去某個(gè)服務(wù)注冊(cè)地查找其依賴(lài)的組件,我們這里重點(diǎn)介紹DI。
IoC的一個(gè)重點(diǎn)是在系統(tǒng)運(yùn)行中,動(dòng)態(tài)的向某個(gè)對(duì)象提供它所需要的其他對(duì)象。這一點(diǎn)是通過(guò)DI來(lái)實(shí)現(xiàn)的。比如對(duì)象A需要操作數(shù)據(jù)庫(kù),以前我們總是要在A(yíng)中自己編寫(xiě)代碼來(lái)獲得一個(gè)Connection對(duì)象,有了spring我們就只需要告訴spring,A中需要一個(gè)Connection,至于這個(gè)Connection怎么構(gòu)造,何時(shí)構(gòu)造,A不需要知道。通過(guò)依賴(lài)注入機(jī)制,我們只需要通過(guò)簡(jiǎn)單的配置,而無(wú)需任何代碼就可指定目標(biāo)需要的資源,完成自身的業(yè)務(wù)邏輯,而不需要關(guān)心具體的資源來(lái)自何處,由誰(shuí)實(shí)現(xiàn)。在系統(tǒng)運(yùn)行時(shí),spring會(huì)在適當(dāng)?shù)臅r(shí)候制造一個(gè)Connection,然后像打針一樣,注射到A當(dāng)中,這樣就完成了對(duì)各個(gè)對(duì)象之間關(guān)系的控制。A需要依賴(lài)Connection才能正常運(yùn)行,而這個(gè)Connection是由spring注入到A中的,依賴(lài)注入的名字就這么來(lái)的。Spring是通過(guò)反射技術(shù)實(shí)現(xiàn)注入的,它允許程序在運(yùn)行的時(shí)候動(dòng)態(tài)的生成對(duì)象、執(zhí)行對(duì)象的方法、改變對(duì)象的屬性。
簡(jiǎn)單的總結(jié)一下依賴(lài)注入:
- 依賴(lài) : 指Bean對(duì)象的創(chuàng)建依賴(lài)于容器 。
- 注入 : 指Bean對(duì)象所依賴(lài)的資源 , 由容器來(lái)設(shè)置和裝配。
注入的方式主要包括Setter注入(重點(diǎn))、構(gòu)造器注入和參數(shù)直接注入。還有拓展方式注入,即:p命名空間注入和c命名空間注入,這里就不再展開(kāi)介紹了,有興趣的同學(xué)可以自行研究。
Setter注入
IoC 容器使用 setter 方法注入被依賴(lài)的實(shí)例。通過(guò)調(diào)用無(wú)參構(gòu)造器或無(wú)參 static 工廠(chǎng)方法實(shí)例化 bean 后,調(diào)用該 bean的setter 方法(類(lèi)中必須有屬性的set方法),即可實(shí)現(xiàn)基于setter的DI
代碼如下:
- public class Address {
- private String address;
- public String getAddress() {
- return address;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- }
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.Set;
- public class Student {
- private String name;
- private Address address;
- private String[] books;
- private List<String> hobbys;
- private Map<String,String> card;
- private Set<String> games;
- private String wife;
- private Properties info;
- public void setName(String name) {
- this.name = name;
- }
- public String getName() {
- return this.name;
- }
- public void setAddress(Address address) {
- this.address = address;
- }
- public void setBooks(String[] books) {
- this.books = books;
- }
- public void setHobbys(List<String> hobbys) {
- this.hobbys = hobbys;
- }
- public void setCard(Map<String, String> card) {
- this.card = card;
- }
- public void setGames(Set<String> games) {
- this.games = games;
- }
- public void setWife(String wife) {
- this.wife = wife;
- }
- public void setInfo(Properties info) {
- this.info = info;
- }
- public void show(){
- System.out.println("name="+ name
- +",address="+ address.getAddress()
- +",books="
- );
- for (String book:books){
- System.out.print("<<"+book+">>\t");
- }
- System.out.println("\nhobbys:"+hobbys);
- System.out.println("card:"+card);
- System.out.println("games:"+games);
- System.out.println("wife:"+wife);
- System.out.println("info:"+info);
- }
- }
配置文件
- <bean id="student" class="com.my.demo.Student">
- <property name="name" value="小明"/>
- </bean>
配置文件中把name 賦值為小明,即完成了對(duì)代碼 private String name的注入。
測(cè)試類(lèi)
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
- Student student=(Student)context.getBean("student");
- System.out.println(student.getName());
- }
運(yùn)行結(jié)果,輸出:小明
常見(jiàn)注入方式的xml 配置如下:
bean注入
使用ref進(jìn)行引入其他bean
- <bean id="student" class="com.my.demo.Student">
- <property name="name" value="小明"/>
- <property name="address" ref="addr"/>
- </bean>
數(shù)組注入
- <property name="books">
- <array>
- <value>數(shù)學(xué)</value>
- <value>語(yǔ)文</value>
- <value>英語(yǔ)</value>
- </array>
- </property>
List注入
- <property name="hobbys">
- <list>
- <value>聽(tīng)歌</value>
- <value>看電影</value>
- <value>打游戲</value>
- </list>
- </property>
Map注入
- <property name="card">
- <map>
- <entry key="招行" value="123456789"/>
- <entry key="工行" value="987654321"/>
- </map>
- </property>
set注入
- <property name="games">
- <set>
- <value>CS</value>
- <value>斗地主</value>
- <value>消消樂(lè)</value>
- </set>
- </property>
Null注入
- <property name="wife"><null/></property>
Properties注入
- <propertyname="info">
- <props>
- <propkey="學(xué)號(hào)">123456</prop>
- <propkey="性別">男</prop>
- <propkey="姓名">小明</prop>
- </props>
- </property>
測(cè)試方法
- public static void main(String[] args) {
- ApplicationContextcontext = new ClassPathXmlApplicationContext("bean1.xml");
- Studentstudent=(Student)context.getBean("student");
- student.show();
- }
運(yùn)行結(jié)果,輸出:
name=小明,address=北京,books=
<<數(shù)學(xué)>> <<語(yǔ)文>> <<英語(yǔ)>>
hobbys:[聽(tīng)歌, 看電影, 打游戲]
card:{招行=123456789, 工行=987654321}
games:[CS, 斗地主, 消消樂(lè)]
wife:null
info:{學(xué)號(hào)=123456, 性別=男, 姓名=小明}
構(gòu)造器注入
指IoC 容器使用構(gòu)造方法注入被依賴(lài)的實(shí)例。基于構(gòu)造器的 DI 通過(guò)調(diào)用帶參數(shù)的構(gòu)造方法實(shí)現(xiàn),每個(gè)參數(shù)代表一個(gè)依賴(lài)。
代碼如下:
- public class Student2{
- private String name;
- public Student2(String name) {
- this.name = name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public void show(){
- System.out.println("name="+ name );
- }
- }
配置文件中設(shè)置
- <!-- 第一種根據(jù)index參數(shù)下標(biāo)設(shè)置 -->
- <bean id="student1" class="com.my.demo.Student2">
- <!-- index是構(gòu)造方法 , 下標(biāo)從0開(kāi)始 -->
- <constructor-arg index="0" value="kevin1"/>
- </bean>
- <!--第二種根據(jù)參數(shù)名字設(shè)置 -->
- <bean id="student2" class="com.my.demo.Student2">
- <!-- name指參數(shù)名 -->
- <constructor-arg name="name" value="kevin2"/>
- </bean>
- <!--第三種根據(jù)參數(shù)類(lèi)型設(shè)置(不推薦使用) -->
- <bean id="student3" class="com.my.demo.Student2">
- <constructor-arg type="java.lang.String" value="kevin3"/>
- </bean>
測(cè)試代碼
- public static void main(String[] args) {
- ApplicationContextcontext = new ClassPathXmlApplicationContext("bean3.xml");
- Student2 user = (Student2) context.getBean("student1")
- user.show();
- }
運(yùn)行結(jié)果:
name=kevin1
參數(shù)直接注入
主要通過(guò)注解@Autowired、@Qualifier和@Resource來(lái)實(shí)現(xiàn)
@Autowired
@Autowired是按類(lèi)型自動(dòng)轉(zhuǎn)配的,不支持id匹配。
需要導(dǎo)入 spring-aop的包
配置文件中設(shè)置<
context:annotation-config/>
代碼:
- public class Animal {
- @Autowired private Cat cat; //運(yùn)行時(shí)spring通過(guò)DI會(huì)把Cat類(lèi)實(shí)例化
- @Autowired private Dog dog;//運(yùn)行時(shí)spring通過(guò)DI會(huì)把Dog類(lèi)實(shí)例化
- public void printCatshot() {
- cat.shout();
- }
- public void printDogshot() {
- dog.shout();
- }
- }
@Qualifier
@Autowired是根據(jù)類(lèi)型進(jìn)行自動(dòng)裝配的。如果當(dāng)Spring上下文中存在一個(gè)類(lèi)型的不同bean時(shí),就會(huì)拋出BeanCreationException異常;我們可以使用@Qualifier配合@Autowired來(lái)解決這些問(wèn)題。
代碼:
- @Autowired
- @Qualifier(value= "dog1")
- private Dog dog1;
- beans.xml
- <bean id="dog1" class="com.my.demo.Dog"/>
- <bean id="dog2" class=" com.my.demo.Dog"/>
@Resource
@Resource是J2EE提供的, 需導(dǎo)入Package: javax.annotation.Resource;
@Resource如有指定的name屬性,先按該屬性進(jìn)行byName方式查找裝配,其次再進(jìn)行默認(rèn)的byName方式進(jìn)行裝配,都不成功,則報(bào)異常。
代碼
- @Resource(name = "dog2")
- private Dog dog;
- beans.xml
- <bean id="dog1" class=" com.my.demo.Dog "/>
- <bean id="dog2" class="com.my.demo.Dog "/>
最簡(jiǎn)單的解釋
IoC通過(guò)DI技術(shù)主要實(shí)現(xiàn)了以下兩點(diǎn):
- 在系統(tǒng)運(yùn)行中,動(dòng)態(tài)地向某個(gè)對(duì)象提供它所需要的其他對(duì)象;
- 在系統(tǒng)運(yùn)行中,動(dòng)態(tài)地從配置文件中讀取數(shù)據(jù)來(lái)為對(duì)象的屬性進(jìn)行賦值。
【編輯推薦】