IOC(Inversion of Control):控制反轉 以下以課程與老師的安排來介紹控制反轉。 一個合理的課程編排系統應該圍繞培訓的內容為核心,而不應該以具體的培訓老師為核心,這樣才能在正常授課時可以隨意選取合適的老師來上課,而非綁定到一個老師身上。 一、探索IOC 1、最緊耦合度的編法 老師 ...
IOC(Inversion of Control):控制反轉
以下以課程與老師的安排來介紹控制反轉。
一個合理的課程編排系統應該圍繞培訓的內容為核心,而不應該以具體的培訓老師為核心,這樣才能在正常授課時可以隨意選取合適的老師來上課,而非綁定到一個老師身上。
一、探索IOC
1、最緊耦合度的編法
public class JavaTrad { public void JavaCourse(){ Bill bill=new Bill();//引入講師,使講師直接侵入培訓 bill.teach("Spring IoC章節!"); } }
public class Bill { public void teach(String saying){ System.out.println(saying); } }
老師和課程一一對應,完全耦合,無法做到將代碼重心放到課程內容上,而且每次修改講師後,需要修改源碼。
2、較緊耦合度介面式編法
public interface Teacher { public void teach(String saying); }
public class Bill implements Teacher{ @Override public void teach(String saying){ System.out.println(saying); } }
public class JavaTrad { public void JavaCourse(){ Teacher teacher=new Bill();//引入教師介面 teacher.teach("Spring IoC章節!");//通過介面進行授課 } }
Bill講師實現Teacher介面,利用多態特性,在課程類中讓Bill講師實現Teacher介面。但是這樣的話課程類就得和Teacher介面、Bill講師類都關聯,並沒有達到我們所期望的授課內容僅依賴與課程類的目的。
所以我們仍然需要找到一個方法來松耦合。
3、引入中介類chairman來松耦合
public interface Teacher { public void teach(String saying); }
public class Bill implements Teacher{ public void teach(String saying){ System.out.println(saying); } }
public class JavaTrad{ private Teacher teacher; public void javaCourse(){ teacher.teach("Spring IoC章節!"); } public void injectTeacher(Teacher teacher) {//聚合 // TODO Auto-generated method stub this.teacher=teacher; } }
public class Chairman { public void managerCourse(){ Teacher teacher=new Bill(); JavaTrad javatrad=new JavaTrad(); javatrad.injectTeacher(teacher); javatrad.javaCourse(); } public static void main(String[] args) { new Chairman().managerCourse(); } }
課程類中只需要引入一個實現老師介面的對象即可,具體是哪個對象實現了老師介面,課程類不用管,這個工作交給了chairman類來分配,解決了具體講師類和課程類之間的緊耦合關係,但是每次換講師之前還得修改chairman代碼,所以還可以有所改進。
二、IoC概念
在實際應用開發中,需要儘量避免和降低對象之間的依賴關係,即降低耦合,一般的業務對象之間,業務對象與持久層之間都存在這樣或那樣的依賴關係。那麼,如何降低對象之間的依賴關係,IoC正式解決這個問題。
在傳統的實現中,由程式內部的代碼來控制對象之間的關係。當一個對象需要依賴另一個對象時,我們用new來創建它的依賴對象,實現兩個組件的組合關係。這種實現方式會造成組件之間的耦合。控制反轉是指應用程式中對象的創建、銷毀等不再由程式本身編碼實現,而是由外部的Spring容器來控制,所以稱為控制反轉。這種控制權的轉移帶來的好處是降低了對象簡單間的依賴關係,即實現瞭解耦。
IoC的字面意思是控制反轉,它包括兩個內容:其一就是控制,其二就是反轉。控制就選擇被調用者類的控制權,反轉指這種控制權從具體的調用者類中移除,轉而交給一個裝配者手中了。
在軟體模式中具體表現為:某一個介面具體實現類的選擇控制權從調用者類中移除,轉交給第三方仲裁。第三方為配置文件,由IOC容器裝載配置文件之後,自動映射生成對象及其屬性。
IoC的具體方法是依賴註入DI(Dependency ):組件之間的依賴關係由容器在運行期間決定,形象的說,即由容器動態的將某種依賴關係註入到主鍵之中。
三、依賴註入DI的三種方式
1、構造函數註入
在構造器註入中,我們通過在調用者類的構造器中將被調用者對象變數賦值即可完成註入
public class Chairman { public void managerCourse(){ Teacher teacher=new Bill(); JavaTrad javatrad=new JavaTrad(teacher); //構造器註入 javatrad.javaCourse(); } public static void main(String[] args) { new Chairman().managerCourse(); } }
public class JavaTrad { private Teacher teacher; public JavaTrad(Teacher teacher){//構造函數註入方法 this.teacher=teacher; } public void javaCourse(){ this.teacher.teach("Spring IoC章節!"); } }
2、set屬性註入方式
在set屬性註入中,我們通過使用調用者的set方法將teacher的具體對象傳入teacher中,即可完成註入。
<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <bean name="teacher" class="com.weikun.IoC6.Bill"></bean> <bean id="trad" class="com.weikun.IoC6.JavaTrad"> <property name="teacher" ref="teacher"></property> </bean> </beans>
public class JavaTrad{ private Teacher teacher; public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } public void JavaCourse(){ this.teacher.teach("Spring IoC章節!"); } }
public class Test { public static void main(String[] args) { Resource resource=new ClassPathResource("applicationContext1.xml");//容器 BeanFactory beanFactory=new XmlBeanFactory(resource); JavaTrad javaTrad=(JavaTrad)beanFactory.getBean("trad");
Teacher teacher=(Teacher)beanFactory.getBean("teacher"); javaTrad.getTeacher().teach("asd"); } }
3、介面註入方式
讓調用者也實現一個介面,介面中定義一個介面註入方法,在具體調用者類中實現它,從而完成屬性的註入(方法類似於自定義一個setter方法)
public class JavaTrad implements Trad{ private Teacher teacher; public void JavaCourse(){ this.teacher.teach("Spring IoC章節!"); } public void injectTeacher(Teacher teacher) {//介面屬性註入方式 // TODO Auto-generated method stub this.teacher=teacher; } }
public interface Trad {
void injectTeacher(Teacher teacher);
}
4、四種依賴註入方式的對比
1)、介面註入
從註入方式的使用上來說,介面註入是不提倡的一種方式,極少被用戶使用。因為它強制被註入對象實現容器的介面,帶有“侵入性”,而構造方法註入和setter方法註入則不需要如此。
2)、setter註入
這種註入方式與傳統的JavaBean寫法很相似,程式員更容易理解和接受,通過setter方式設定依賴關係顯得更加直觀自然。
缺點是組件使用者或許會忘記組件註入需要的依賴關係,同時依賴可能會因為setter方法的調用而被修改。
3)、構造註入
這種方法的優點是:構造註入可以在構造器中決定依賴關係的註入順序。依賴關係只能在構造器中設定,則只有組件的創建者才能改變組件的依賴關係。對組件的調用者而言,組件內部的依賴關係完全透明。
缺點:對於複雜的依賴關係如果採用構造註入,會導致構造器過於臃腫,難以閱讀。
5、IoC的裝載機制
1、IoC容器
Spring提供了強大的IoC容器來管理組成應用程式中的Bean,要利用容器提供的服務,就必須配置Bean,配置文件描述了Bean的定義和他們之間的依賴關係。Spring通過大量引入了Java的反射機制動態生成Bean對象並註入到程式中避免硬編碼,實現了該功能的核心組件是BeanFactory,而ApplicationContext繼承了BeanFactory介面,提供了更多的高級特性,建議優先使用後者。
ApplicationContext的實現類如下:
1)、ClassPathXmlApplicationContext:從CLASSPATH下載入文件
2)、FileSystemXmlApplicationContext:從文件系統中載入文件
首先,可以通過任一實現類來將配置文件中定義的Bean載入到容器中,如:(這裡的ctx就是容器)
1)、ApplicationContext ctx=new ClassPathXmlApplicationContext("bean.xml");
2)、ApplicationContext ctx=new FileSystemXmlApplicationContext("classpath:bean.xml");
然後,通過下列語句獲取Bean的實例
Moveable animal=(Moveable) ctx.getBean("animal");
6、IoC的零配置註解寫法
如果Bean不是自己編寫的類(如SessionFactory),註解配置將無法實施,此時XML配置是唯一可以使用的方式
1)、@Autowired
它對類的成員變數、方法以及構造方法進行標註,完成自動裝配的工作。
import org.springframework.beans.factory.annotation.Autowired; public class Animal{
Private MessagePriter printer; @Autowired public void setPrinter(MessagePriter printer){ this.priter=printer; } }
<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd "> <bean id="movePrinter" class="com.zxc.A.movePriter"></bean> <bean id="animal" class="com.zxc.A.JavaTrade"> <property name="animalType" value="Bird"/> <property name="moveMode" value="fly"/> </bean> </beans>
2)、@Qualifier
如果一個bean的屬性可能來自多個其他的候選bean,導致Spring無法確定使用那一個bean,當Spring容器在啟動時就會拋出BeanCreationException異常。Spring允許我們通過@Qualifie註解指定註入bean的名稱。
import org.springframework.beans.factory.annotation.Autowired; public class Animal{ @Autowired Private @Qualifier(value="screen")MessagePriter printer; }
通常@Qualifer和@Autowired結合使用,且置於成員變數類型的前面。
3)、@Resource
@Resource的作用相當於@Autowired,只不過@Autowired按byType自動註入的,而@Resource預設按byName自動註入罷了。
4)、@Component
雖然可以通過@Autowired或@Resource在Bean類中使用自動註入功能,但是還是需要在XML中定義<bean>。而通過註解的@Component就可以通過註解就定義Bean,在類前加上@Component