深圳網(wǎng)站建設(shè)好站長工具是干嘛的
接下來先簡單說一下關(guān)于IoC的一些要點(diǎn),后面我們再詳細(xì)一步一步討論。
一、IoC控制反轉(zhuǎn)
- IoC控制反轉(zhuǎn)它是一種思想,不是具體的實(shí)現(xiàn)
- 控制反轉(zhuǎn)的目的是為了降低程序的耦合度,提高程序的可擴(kuò)展性,從而滿足OCP原則和DIP原則
- 控制反轉(zhuǎn),那到底反轉(zhuǎn)是什么東西?
- 我們不再使用某個對象去直接使用代碼去new了,對于這種對象的創(chuàng)建權(quán)利交出去了。
- 對象與對象之間關(guān)系的維護(hù)交出去了
- 控制反轉(zhuǎn)這種思想如何實(shí)現(xiàn)的呢?在Spring框架當(dāng)中使用DI(依賴注入)實(shí)現(xiàn)
二、依賴注入
依賴注入實(shí)現(xiàn)了控制反轉(zhuǎn)思想,在Spring當(dāng)中就是使用依賴注入的方式來實(shí)現(xiàn)對Bean的管理的。
Bean管理,是管理啥子?
既然依賴注入是實(shí)現(xiàn)了控制反轉(zhuǎn)這種思想,那么Bean管理就是對這種思想的實(shí)際體現(xiàn),所以管理的就是Bean對象的創(chuàng)建;Bean對象中屬性的賦值(Bean對象之間的關(guān)系)。
我們來拆解一下依賴注入:
- 依賴:對象與對象之間的關(guān)聯(lián)關(guān)系
- 注入:指的是一種數(shù)據(jù)傳遞的行為,通過注入這種行為來讓對象與對象之間產(chǎn)生關(guān)系
常見的依賴注入實(shí)現(xiàn)方式有:set注入與構(gòu)造注入
set注入
新增實(shí)例來說明具體的實(shí)現(xiàn)
新增模塊,pom文件中添加依賴
<!-- spring context -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.9</version>
</dependency><!--junit-->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope>
</dependency>
新增Dao類
public class UserDao {public void insert() {System.out.println("正在保存用戶數(shù)據(jù)...");}
}
新增Service類,這里service要依賴UserDao實(shí)例對象
public class UserService {private UserDao userDao;// 使用set方式注入,必須提供set方法// 反射機(jī)制要調(diào)用這個方法為屬性賦值public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void save() {userDao.insert();}
}
新增Spring的配置文件spring.xml
在這個配置文件中,我們就要定義兩個bean,同時要聲明兩個bean中的依賴關(guān)系,從下面的配置可以看到我們在聲明UserService類對應(yīng)的bean時,其中就使用set注入,這個與我們實(shí)際代碼是對應(yīng)的UserService類中有一個屬性要求是UserDao類對象。
<?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"><bean id="userDaoBean" class="com.xiaoxie.dao.UserDao"/><bean id="userServiceBean" class="com.xiaoxie.service.UserService"><!-- set注入 --><property name="userDao" ref="userDaoBean"/></bean></beans>
新增測試類
public class DiTest {@Testpublic void testSetDI() {ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");UserService userService = context.getBean("userServiceBean", UserService.class);userService.save();}
}
通過運(yùn)行上面的測試方法可以正常通過service對象去調(diào)用到內(nèi)save()方法
其上實(shí)現(xiàn)的原理:
- 在spring配置文件中,通過property標(biāo)簽獲取到屬性名:userDao
- 通過屬性名可以推斷出對應(yīng)的set方法:setUserDao()
- 通過反射機(jī)制調(diào)用setUserDao()方法給對應(yīng)的屬性賦值
- property標(biāo)簽的name屬性指的就是屬性名
- property標(biāo)簽的ref屬性就是要注入的bean對象的id,這樣的話就通過ref屬性來完成bean的裝配
這里說的裝配,相當(dāng)于一個系統(tǒng)有多個組件,這多個組件按一定的要求和規(guī)則組裝到一起的過程。
此時如果我們把UserService類中的userDao的set方法刪除后,再運(yùn)行測試方法,程序會報異常:Bean property 'userDao' is not writable or has an invalid setter method.
在解析配置文件推斷set方法的邏輯是,通過property中的name屬性的值來推斷,如果值是userDao,那么推斷的方式是:"set" + "userDao".substring(0,1).toupperCase() + "userDao",substring(1) ===> setUserDao
只要在要進(jìn)行依賴注入的類中存在推斷出來的方法即可,它在裝配時就會調(diào)用這個方法,通過這個方法來進(jìn)行屬性的賦值,打比方,我們把代碼修改為如下:
public class UserService {private UserDao userDao;public void setAbc(UserDao userDao) {this.userDao = userDao;}public void save() {userDao.insert();}
}
那么對應(yīng)的配置文件也應(yīng)該同步修改為如下:
<?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"><bean id="userDaoBean" class="com.xiaoxie.dao.UserDao"/><bean id="userServiceBean" class="com.xiaoxie.service.UserService"><!-- set注入 --><property name="abc" ref="userDaoBean"/></bean></beans>
構(gòu)造注入
構(gòu)造注入指的是在裝配時通過構(gòu)造方法來給指定的屬性賦值
新增OrderDao類
public class OrderDao {public void createOrder() {System.out.println("正在創(chuàng)建訂單...");}
}
新增OrderService類
public class OrderService {private OrderDao orderDao;public OrderService(OrderDao orderDao) {this.orderDao = orderDao;}public void create() {orderDao.createOrder();}
}
spring配置文件中進(jìn)行構(gòu)造方法注入的配置
<bean id="orderDaoBean" class="com.xiaoxie.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.xiaoxie.service.OrderService"><constructor-arg index="0" ref="orderDaoBean"/>
</bean>
測試類中進(jìn)行測試
OrderService orderService = context.getBean("orderServiceBean", OrderService.class);
orderService.create();
對于構(gòu)造方法注入,使用的標(biāo)簽是:<constructor-arg>
因?yàn)橐⑷?,所以要指定哪個bean實(shí)例賦值給那個屬性進(jìn)行裝配,這個裝配的“圖紙”一定要搞清楚,要不然會出錯,那這里要如何對應(yīng)呢?
對應(yīng)關(guān)系可以通過如下幾種方式來指定:
- 通過下標(biāo)來指定,比如上例中的index屬性,下標(biāo)是從0開始的,0表示構(gòu)造方法中的第一個參數(shù),1表示第二個參數(shù),依次類推
- 通過參數(shù)名稱來指定,比如上列中還可以修改為<constructor-arg name="orderDao" ref="orderDaoBean"/>
- 也可以不指定下標(biāo)和參數(shù)名,比如上列中直接寫為<constructor-arg ref="orderDaoBean"/>
注意:當(dāng)我們既不指定index也不提定nane時,只是指定了ref(待注入的bean)時要保證構(gòu)造方法中的各個參數(shù)類型是唯一的,因?yàn)檫@個時候spring是根據(jù)參數(shù)的類型與指定ref的類型去匹配注入的!
在我們實(shí)際使用中set注入是比較常用的,接下來專門以set注入作為一個專題來詳細(xì)聊聊依賴注入
三、詳聊set注入
注入外部Bean(常用)
前面我們在舉例set注入的時候就是一種外部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"><bean id="userDaoBean" class="com.xiaoxie.dao.UserDao"/><bean id="userServiceBean" class="com.xiaoxie.service.UserService"><property name="userDao" ref="userDaoBean"/></bean>
</beans>
它的特點(diǎn)就是,待注入的bean是定義在當(dāng)前bean的外部的,在當(dāng)前bean中要注入它的時候使用ref來進(jìn)行注入。一般來說都是采用這種方式進(jìn)行注入,單獨(dú)把bean定義在外部可以提高這個bean的復(fù)用性,誰知道有沒有其它的bean要注入這個bean呢?
注入內(nèi)部Bean(使用少)
<!-- 使用內(nèi)部bean注入 -->
<bean id="userServiceBean" class="com.xiaoxie.service.UserService"><property name="userDao"><bean class="com.xiaoxie.dao.UserDao"/></property>
</bean>
這種方式在<property>
標(biāo)簽內(nèi)部再寫一個bean標(biāo)簽,使用這種方式進(jìn)行注入叫注入內(nèi)部Bean,這個內(nèi)部的bean只在這個bean可以訪問到&#