控制反轉(Inversion of Control,英文縮寫爲IoC)是一個重要的面向對象編程的法則來削減計算機程序的耦合問題,也是輕量級的Spring框架的核心。 控制反轉通常分爲兩種類型,依賴注入(Dependency Injection,簡稱DI)和依賴查找。依賴注入應用比較普遍,咱們這裏只介紹依賴注入。java
1、IOC簡介web
控制反轉IOC,它最主要反映的是與傳統面向對象(OO)編程的不一樣。一般咱們編程實現某種功能都須要幾個對象相互做用,從編程的角度出發,也就是一個主對象要保存其餘類型對象的引用,經過調用這些引用的方法來完成任務。如何得到其餘類型的對象引用呢?一種方式是主對象內部主動得到所需引用(也就是一般咱們使用的new一個對象);另外一種方式是在主對象中設置setter 方法,經過調用setter方法或構造方法傳入所需引用。後一種方式就叫IOC,也是咱們經常所說的依賴注入DI。如下咱們用一個簡單的例子來講明傳統OO編程與IOC編程的差異。spring
這個例子的目的是根據時間不一樣返回不一樣的問候字符串, 好比Good Morning, world或Good afternoon, World。apache
服務接口:編程
package cn.test.ioc; public interface HelloIF { String sayHello(); }
傳統實現:設計模式
package cn.test.ioc; import java.util.Calendar; /** * 傳統實現(非IOC方式) * @author Administrator * */ public class HelloIFImpl implements HelloIF { private Calendar cal; // 咱們須要的引用 public HelloIFImpl() { cal = Calendar.getInstance(); // 主動獲取 } public String sayHello(){ if(cal.get(Calendar.AM_PM) == Calendar.AM){ return "Good morning, World"; }else{ return "Good afternoon, World"; } } public static void main(String args[]){ HelloIFImpl hf = new HelloIFImpl(); System.out.println(hf.sayHello()); } }
採用IOC方式:app
package cn.test.ioc; import java.util.Calendar; /* * IOC方式實現 */ public class HelloIFImpl2 implements HelloIF { private Calendar cal; // 咱們須要的引用 public void setCal(Calendar cal) { this.cal = cal; } // 依賴注入 public String sayHello(){ if(cal.get(Calendar.AM_PM) == Calendar.AM){ return "Good morning, World"; }else{ return "Good afternoon, World"; } } public static void main(String args[]){ HelloIFImpl2 hf = new HelloIFImpl2(); hf.setCal(Calendar.getInstance()); System.out.println(hf.sayHello()); } }
在這裏你也許會問:我看不出有太大差異,而且依賴注入還須要我先建立外部的Calendar對象,而後再傳到HelloIFImpl對象中。可是,假如咱們事先已經在類用new orderOracle(),可是後來因爲需求變動,咱們須要使用new orderSqlServer(),這樣咱們還須要修改全部使用orderOracle()的類,這樣好麻煩。若是咱們使用IOC方法編程而且使用了spring框架,這樣咱們只須要修改xml配置文件便可。框架
IoC則是一種 軟件設計模式,它告訴你應該如何作,來解除相互依賴模塊的耦合。控制反轉(IoC),它爲相互依賴的組件提供抽象,將依賴(低層模塊)對象的得到交給第三方(系統)來控制,即依賴對象不在被依賴模塊的類中直接經過new來獲取。jsp
2、IOC的一個應用舉例測試
下面咱們以一個struts2和Spring整合的例子來講明。
1)整合struts2和Spring
首先要整合Spring和Struts2,須要先要拷入Spring須要的jar包,既包括Spring自己的jar包,也包括Struts2的Spring插件。將如下幾個jar包拷入到咱們的web工程的WEB-INF\lib中:org.springframework.asm-3.0.5.RELEASE.jar ;spring-*.jar(struts2中lib包裏全部的jar,共6個); struts2-spring-plugin-*.jar 。
2)編寫邏輯層接口
package cn.test.springDemo; public interface SampleService { public String getNameById(String userId); }
3)編寫邏輯層實現類
package cn.test.springDemo; public class SampleServiceImpl implements SampleService{ public String getNameById (String userId) { //根據userId到數據層進行查詢,獲取相應的name String name = "hello,"+ userId; return name; } }
4)編寫ACTION
package cn.test.springDemo; import com.opensymphony.xwork2.ActionSupport; public class SampleAction extends ActionSupport { // 經過setter方式,由Spring來注入SampleService實例 private SampleService service; public void setService(SampleService service) { this.service = service; } private String name; private String userId; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String execute() throws Exception { name = this.service.getNameById(userId); return SUCCESS; } }
在execute方法中再也不直接new一個SampleServiceImpl的實例了,而是聲明瞭一個SampleSerivce類型的屬性,並提供對應的setter方法,這個setter方法是留給Spring注入對象實例的時候調用的,能夠不用提供getter方法。也就是說,如今的SampleAction已經不用知道邏輯層的具體實現了。
5)編寫Spring的配置文件applicationContext.xml
要讓Spring來管理SampleAction和SampleServiceImpl的實例,還須要新建一個Spring的配置文件。在src下新建一個applicationContext.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-2.5.xsd"> <bean name="service" class="cn.test.springDemo.SampleServiceImpl" /> <bean name="sampleAction" class="cn.test.springDemo.SampleAction" scope="prototype" > <property name="service" ref="sampleService"/> </bean> </beans>
這個xml的根元素是<beans>,在<beans>中聲明瞭它的schema引用,除此以外,還有兩個<bean>元素,定義了由Spring管理的SampleServiceImpl和SampleAction。
l name屬性爲它設置了一個名字
l class元素指定了它的實現類的全類名
l name屬性和class屬性的含義與第一個<bean>元素徹底同樣。
l scope屬性,賦值爲prototype(原型)。scope屬性很是重要,它管理了註冊在它裏面的Bean的做用域。Spring容器默認的做用域是單例,即每次外界向Spring容器請求這個Bean,都是返回同一個實例;可是,Struts2的Action是須要在每次請求的時候,都要新建一個Action實例,因此,在配置對應Action的<bean>元素時,必須把它的scope屬性賦值爲prototype,以保證每次請求都會新建一個Action實例。
l <property>子元素。<property>元素的name屬性爲service,表明SampleAction這個類有一個setter方法叫setSampleService;<property>元素的ref屬性爲sampleService,表明Spring容器會將一個名爲sampleService的已經存在的Bean,注入給sampleAction的service屬性。
6)在web.xml中引用Spring配置文件
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>Struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>Struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
listener實現了當這個Web工程啓動的時候,就去讀取Spring的配置文件,這個類是由Spring提供的,這裏只須要配置上去就能夠了。上下文參數的配置裏面,contextConfigLocation的值classpath*:applicationContext.xml,代表了全部出如今classpath路徑下的applicationContext.xml文件,都是上面的這個Listener要讀取的Spring配置文件。
7)編寫struts.xml
就快要大功告成了,最後一步,來修改struts.xml,須要作兩件事:
首先,添加常量struts.objectFactory,其值爲spring,這就指定了Struts2使用Action的時候並非本身去新建,而是去向Spring請求獲取Action的實例。示例以下:
<constant name="struts.objectFactory" value="spring"/>
而後,<action>元素的class屬性,如今並非要填Action類的全類名了,而是要填一個在Spring配置文件中配置的Action的Bean的名字,也就是<bean>元素的name屬性,很顯然,須要的是sampleAction這個Bean。示例以下:
<package name="springPackage" extends="struts-default"> <action name="sampleActionName" class="sampleAction"> <result>/spring/success.jsp</result> </action>
</package>
只有<action>元素的class屬性變了,其餘部分不變。來測試一下,運行:http://localhost:8080/struts2Deepen2/sampleActionName.action?userId=test。【其中,struts2Deepen2爲web工程的名稱】。運行一切正常,對吧,這也說明Struts2與Spring整合並非爲了實現新功能,而是爲了讓表現層組件和邏輯層組件解耦,SampleAction類不用再知道SampleServiceImpl這個具體實現了,只須要知道SampleService這個接口就能夠了。
參考資料:
http://blog.csdn.net/Kettas2008/article/details/2447809
http://www.iteye.com/topic/1124526
http://www.cnblogs.com/liuhaorain/p/3747470.htm#title_4 【這篇文章詳細介紹了DIP、IoC、DI以及IoC容器,推薦看看】