Spring學習--IOC依賴注入
最近在玩SpringBoot,因為要做分享,所以拿出Spring去做個對比,才能感受到使用SpringBoot的簡單和快速性。
一.Spring定義
Spring是一個開源框架,為了解決企業應用開發的複雜性而建立的,但現在已經不止應用與企業應用。
Spring是一個輕量級的控制反轉(IOC)和麵向切面程式設計(AOP)的容器框架。
--從大小與開銷方面看來,Spring都是輕量級的;
--通過控制反轉IOC的技術達到鬆耦合的目的(把控制權交出去,在使用的時候直接用);
--提供了面向切面程式設計的豐富支援,允許通過分離應用的業務邏輯與系統級服務進行內聚性的開發;
--包含並管理應用物件的配置和生命週期,這個意義上是一種容器;
--將簡單的元件配置組合成複雜的應用,這個意義上是框架。
二.Spring作用
- 容器;
- 提供了多種技術的支援(JMS,MQ支援,UnitTest等);
- AOP(事務管理,日誌管理等);
- 提供了眾多方便應用的輔助類(JDBC Template等);
- 對主流應用框架提供了良好的支援(Hibernate等)。
三.適用範圍
- 構建企業級應用;
- 單獨使用Bean容器(Bean管理);
- 單獨使用AOP進行切面管理;
- 其他的Spring功能,如對訊息的支援等;
- 在網際網路中的應用。
四.IOC控制反轉
控制權的轉移,應用程式本身不負責依賴物件的建立和維護,而是由外部容器來負責建立和維護,DI(依賴注入)是其一種實現方式。
目的:建立物件並組裝物件間的關係。
依賴注入:由IOC容器在執行中,動態的將某種依賴關係注入物件中。
五.Spring的常用注入方式
Spring注入是指在啟動spring容器載入bean配置時,完成對變數的賦值注入。
(1)設值注入(set注入):
配置檔案如下:
property 表示InjectionServiceImpl有一個屬性(成員變數)injectDAO
ref表示引用id
通過set注入,自動呼叫set方法,InjectServiceImpl中一定有一個setInjectDAO方法。
<?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="injectionService" class="com.spring.beananocation.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"></property>
</bean>
<bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>
</beans>
使用程式碼如下:
package com.spring.beananocation;
/**
* DAO介面
*/
public interface InjectionDAO {
String save(String string);
}
package com.spring.beananocation;
/**
* DAO實現類
*/
public class InjectionDAOImpl implements InjectionDAO {
@Override
public String save(String string) {
return "這是一個儲存:" + string;
}
}
package com.spring.beananocation;
/**
* service 介面
*/
public interface InjectionService {
void save(String arg);
}
package com.spring.beananocation;
/**
* service 實現類
*/
public class InjectionServiceImpl implements InjectionService {
/**
* 和配置檔案中property同名的成員變數
*/
private InjectionDAO injectionDAO;
/**
* set方法
*/
public void setInjectionDAO(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
@Override
public void save(String arg) {
//模擬業務操作
System.out.println("service接收引數:" + arg);
arg = arg + " :" + this.hashCode();
System.out.println(injectionDAO.save(arg));
}
}
單元測試方法:
@Test
public void testSetter() {
InjectionServiceImpl injectionService = super.getBean("injectionService");
injectionService.save("這是要儲存的資料");
}
可以看到單元測試執行成功,injectionDAO被成功注入,執行結果如下:
(2)構造注入:
使用構造方法建立類的例項化時,把相應的injectionDAO注入。
配置檔案如下:
<?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="injectionService" class="com.spring.beananocation.InjectionServiceImpl">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
<bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>
</beans>
將InjectServiceImpl的set方法變為構造方法來注入:
package com.spring.beananocation;
/**
* service 實現類
*/
public class InjectionServiceImpl implements InjectionService {
/**
* 和配置檔案中property同名的成員變數
*/
private InjectionDAO injectionDAO;
/**
* 構造方法
*/
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
@Override
public void save(String arg) {
//模擬業務操作
System.out.println("service接收引數:" + arg);
arg = arg + " :" + this.hashCode();
System.out.println(injectionDAO.save(arg));
}
}
單元測試方法和結果同設定注入。
建議以設值注入為主,構造注入為輔的方式來進行注入。
對於依賴關係無需變化的注入,儘量採用構造注入;而其他依賴關係的注入,則優先考慮設值注入。
五.Bean的配置項
- id:在整個ioc容器中,這個Bean的唯一標識;
- class:具體要例項化的哪一個類;
- scope:指範圍,作用域;
- Constructor arguments:構造器的引數(構造注入用)。
- Properties:屬性(設值注入用)。
- AutoWiring mode:自動裝配模式。
- lazy-initialization mode:懶載入模式。
- Initialization/destruction method:初始化和銷燬方法。
六.Bean的作用域
- singleton :單例模式,預設選項,指一個Bean容器中只存在一份。(啟動時就建立。)
- prototype:每次請求(每次使用)建立新的例項,destory方法不生效,因為請求完成後,例項就被垃圾回收器回收了,不存在了。(在用到物件時才建立。)
- request:每次http請求建立一個例項且僅在當前request內有效。
- session:同request,每次http請求建立,當前session內有效。
- global session:基於portlet的web中有效(portel定義了global session),如果是在web中,同session。(有時候做系統和應用的整合,單點登入等。)
在配置檔案中加scope配置項:
<?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="injectionService" class="com.spring.beananocation.InjectionServiceImpl" scope="singleton">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
<bean id="injectionDAO" class="com.spring.beananocation.InjectionDAOImpl"></bean>
</beans>
修改單元測試用例如下,由於我們打出了hashcode:
/**
* 單元測試
*/
@Test
public void testSetter() {
InjectionServiceImpl injectionService = super.getBean("injectionService");
injectionService.save("這是要儲存的資料");
InjectionServiceImpl injectionService2 = super.getBean("injectionService");
injectionService2.save("這是要儲存的資料");
}
所以 當 scope="singleton"或者不加scope配置項時,為單例模式。
所以兩次輸出的hashcode為相同的,如下:
而當scope="prototype",每次使用都是一個新的例項。輸出的hashcode不一致,如下:
七.Bean 的生命週期
- 定義:在Spring的bean的xml檔案中配置的bean,定義了一個id和class;
- 初始化:當ioc容器在啟動的時候去載入bean配置檔案裡面的bean並初始化,生成bean的例項。
- 使用:從bean容器中取出一個bean的例項,去呼叫它的方法。
- 銷燬:在bean容器停止的時候,去銷燬由當前這個bean建立的所有例項。
Bean初始化和銷燬的三種方式:
(1)配置init-method和destroy-method方法。
<?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="beanLifeCycle" class="com.spring.ioc.impl.BeanLifeCycle" init-method="init"
destroy-method="beanDestroy"></bean>
</beans>
BeanLifeCycle類:
package com.spring.ioc.impl;
public class BeanLifeCycle {
public void init() {
System.out.println("bean start");
}
public void beanDestroy() {
System.out.println("bean stop");
}
public void hello() {
System.out.println("hello~");
}
}
單元測試方法:
@Test
public void beanLifeCycle(){
BeanLifeCycle beanLifeCycle = super.getBean("beanLifeCycle");
beanLifeCycle.hello();
}
單元測試執行結果如下:
可以看到是先初始化,再使用,再銷燬。
(2)實現InitializingBean和DisposableBean介面,並重寫他們的afterPropertiesSet()和destroy()方法。不用修改配置檔案。
package com.spring.ioc.impl;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BeanLifeCycle implements InitializingBean, DisposableBean {
public void hello() {
System.out.println("hello~");
}
@Override
public void destroy() throws Exception {
System.out.println("bean DisposableBean");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("bean InitializingBean");
}
}
(3)在配置檔案配置全域性預設初始化銷燬方法:
配置default-init-method和default-destroy-method屬性。
<?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"
default-init-method="defaultInit" default-destroy-method="defaultDestory">
<bean id="beanLifeCycle" class="com.spring.ioc.impl.BeanLifeCycle"></bean>
</beans>
BeanLifeCycle類:
package com.spring.ioc.impl;
public class BeanLifeCycle{
public void defaultInit() {
System.out.println("defaultInit start");
}
public void defaultDestory() {
System.out.println("defaultDestroy stop");
}
public void hello() {
System.out.println("hello~");
}
}
當3種方式都實現的時候,先執行實現介面的方法,再執行單獨配置的init,destory方法,全域性預設的方法不實現。
(只要實現了單獨配置的init和destory方法,就不再實現全域性預設方法了。)
八.Bean的自動裝配(Autowiring)
- NO:不做任何配置(預設選項);
- byName:根據屬性名自動裝配。此選項將檢查容器並根據名字查詢與屬性完全一致的bean,並將其與屬性自動裝配。bean id重複,會啟動失敗,提示同一個XML下不能存在相同的bean。
- byType:如果容器種存在一個與指定屬性型別相同的bean,那麼將與該屬性自動裝配,如果存在多個該型別的bean,那麼丟擲異常,並指出不能使用byType方式進行自動裝配,如果沒找到相匹配的bean,則什麼事都不發生。
- Constructor:與byType方式類似,不同之處在於它應用於構造器引數。如果容器種沒有找到與構造器引數型別一致的bean,那麼丟擲異常。
在配置檔案中加入配置default-autowire="byName"或者default-autowire="byType",都是通過set方法來賦值的。
<?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"
default-autowire="byName">
<bean id="autoWiringService" class="com.spring.ioc.autowiring.AutoWiringService"></bean>
<bean id="autoWiringDAO" class="com.spring.ioc.autowiring.AutoWiringDAO"></bean>
</beans>
AutoWiringService:
package com.spring.ioc.autowiring;
public class AutoWiringService {
private AutoWiringDAO autoWiringDAO;
public void setAutoWiringDAO(AutoWiringDAO autoWiringDAO) {
System.out.println("走set方法");
this.autoWiringDAO = autoWiringDAO;
}
public void say(String words) {
this.autoWiringDAO.say(words);
}
}
AutoWiringDAO:
package com.spring.ioc.autowiring;
public class AutoWiringDAO {
public void say(String word) {
System.out.println("AutoWiringDAO:" + word);
}
}
單元測試:
@Test
public void autoWiringServiceTest() {
AutoWiringService autoWiringService = super.getBean("autoWiringService");
autoWiringService.say("This is a test!");
}
執行結果:
在配置檔案中加入配置default-autowire="constructor"或者default-autowire="byType",是通過構造方法來賦值的。
AutoWiringService:
package com.spring.ioc.autowiring;
public class AutoWiringService {
private AutoWiringDAO autoWiringDAO;
public AutoWiringService(AutoWiringDAO autoWiringDAO) {
System.out.println("走構造方法");
this.autoWiringDAO = autoWiringDAO;
}
public void say(String words) {
this.autoWiringDAO.say(words);
}
}
九.Bean裝配之Resources
Resources為針對資原始檔的統一介面
- UrlResource:URL對應的資源,根據一個URL地址即可構建。
- ClassPathResource:獲取類路徑下的資原始檔。
- FileSystemResource:獲取檔案系統裡面的資源。
- ServletContextResource:ServletContext封裝的資源,用於訪問ServletContext環境下的資源。
- InputStreamResource:針對於輸入流封裝的資源。
- ByteArrayResource:針對於位元組組封裝的資源。