spring 控制反轉/依賴注入(IOC/DI)
此篇所用ide:idea201801
1)什麼是控制反轉
傳統的編程思路是,當我需要某個對象時,我便自己去實例化調用它 。而控制反轉則是,當我需要某個對象時,自然有人幫我們實例化它 。簡單的來說,這是一種衣來張口,飯來伸手式的控制模式,這也符合spring最根本的使命--簡化java開發 。
2)什麼是依賴注入
依賴注入,即是SpringIOC的一種實現方式,在我們完成業務開發的過程中,需要引入的依賴,都由交由spring容器管理注入 。簡單的說,就是由spring容器替我們實例化對象 。
3)依賴注入解決了什麼問題
在傳統的業務實現中,一個類是解決不了所有問題的,在一個類中我們通常會引入許多其它類來滿足我們的業務需求,這個時候我們避免不了去實例化不同的對象,而面臨需求變更時,相關的改動往往是牽一髮而動全身,這時候我們希望有一種能讓類與類之間的聯繫變得不那麼密切的方式來修改我們的代碼 。這也就是依賴注入給我們帶來的最大收益--松耦合 。
4)依賴注入的實現
首先新建一個maven項目,引入spring核心依賴和其他相關依賴:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>ice-maple</groupId> <artifactId>ice.maple</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- Spring依賴 --> <!-- 1.Spring核心依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.11.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.11.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.11.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- 4.Spring test依賴:方便做單元測試和集成測試 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.11.RELEASE</version> </dependency> </dependencies></project>
假設我們的同事給我們提供了一個獲取用戶的介面,我們需要調用它進行顯示,傳統意義上我們可能需要這麼做:
public class UserService implements IUserService { private IUserDao userDao = new UserDao(); public List<User> getUsers(){ return userDao.getUsers(); }}
而測試的時候我們會這麼做:
@Test public void getUsers() {// ContextConfiguration contextConfiguration = new ContextConfiguration().locations("classpath:spring-config.xml"); IUserService userService = new UserService(); userService.getUsers().forEach(n -> { System.out.println(n.getName()); System.out.println(n.getSex()); }); }
當我們採用這種方式完成業務,此時若有一個需求導致同事更換了實現類,那麼在他新建類的同時,不論是測試還是我們的介面調用,都要做同步修改,由於我們在編寫代碼時類之間的緊密聯繫,導致我們可能在一個小小的業務改動上可能要進行多方的代碼改動,而大多數時候這些類是多個不同的人寫的,依賴注入能很好的解決我們這個問題 。
我們在resource下新建spring-config配置文件,並對service類進行修改:
spring-config.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" 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="ice.maple"/>--> <bean id="UserService" class="ice.maple.service.impl.UserService"> <property name="userDao"> <ref bean="UserDao"></ref> </property> </bean> <bean id="UserDao" class="ice.maple.dao.impl.UserDao"></bean></beans>
UserService
public class UserService implements IUserService { private IUserDao userDao; public List<User> getUsers(){ return userDao.getUsers(); } public IUserDao getUserDao() { return userDao; } public void setUserDao(IUserDao userDao) { this.userDao = userDao; }}
測試類:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:spring-config.xml")//@ContextConfiguration(classes = SpringConfig.class)public class UserServiceTest { @Autowired UserService userService; @Test public void getUsers() { userService.getUsers().forEach(n -> { System.out.println(n.getName()); System.out.println(n.getSex()); }); }}
運行我們可能看到得到了想要的結果,說明注入成功:
當然我們也可以用構造方法的方式注入,service類增加構造方法並對配置文件做部分改動:
UserService
public UserService(IUserDao userDao) { this.userDao = userDao; }
spring-config.xml
<bean id="UserService" class="ice.maple.service.impl.UserService"> <constructor-arg name="userDao"> <ref bean="UserDao"></ref> </constructor-arg> </bean>
測試類不做改動,運行的效果是相同的 。
我們也可以不使用這種註冊bean的方式,因為這種註冊方式事實上並沒有給我們簡化代碼,只是方便了我們的同意管理,我們可以換一種方式實現它,在dao和service上分別加上@Repository和@Service的註解,可以去掉構造方法和getter,setter方法,修改配置文件
spring-config.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="ice.maple"/></beans>
UserService
@Servicepublic class UserService implements IUserService { @Autowired private IUserDao userDao; public List<User> getUsers(){ return userDao.getUsers(); }}
運行測試類,正常啟動 。
當然我們也可以不需要配置文件,直接採用配置類的方式做這件事情
新建SpringConfig類
import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScan("ice.maple")public class SpringConfig {}
修改測試類:
@RunWith(SpringJUnit4ClassRunner.class)//@ContextConfiguration(locations = "classpath:spring-config.xml")@ContextConfiguration(classes = SpringConfig.class)public class UserServiceTest { @Autowired UserService userService; @Test public void getUsers() { userService.getUsers().forEach(n -> { System.out.println(n.getName()); System.out.println(n.getSex()); }); }}
運行測試類,一切正常 。
接下來,我們看看如果spring掃描到了兩個實現類會發生什麼:
我們新建一個PersonDao並加上@Repository註解,啟動測試類 。
運行發現並沒有報錯,spring選擇了UserDao作為實現類,我們再修改一下UserDao的類名 。
再次運行發現,當實現類為兩個類名與介面無關無關的類時,將會報錯,原因是找到了兩個不同的實現類 。這時候我們可以修改註解指定這個實現類:
@Repository("userDao")
(以上內容為個人觀點,如有錯誤請不吝指出)
本篇github地址:
下載戳我
推薦閱讀:
※Spring AOP,IOC,MVC的基本原理
※為什麼要用spring的IOC和AOP?用了IOC和AOP的優點缺點是什麼