好程式設計師Java乾貨分享Spring框架之IOC原理
Spring框架簡介
Spring是一種輕量級的控制反轉(IOC)和麵向切面程式設計(AOP)的容器框架,能夠為企業級開發提供一站式服務。
Spring的優點有
1.方便解耦,簡化開發
通過Spring提供的IoC容器,我們可以將物件之間的依賴關係交由Spring進行控制,避免硬編碼所造成的過度程式耦合。有了Spring,使用者不必再為單例項模式類、屬性檔案解析等這些很底層的需求編寫程式碼,可以更專注於上層的應用。
2.AOP程式設計的支援
通過Spring提供的AOP功能,方便進行面向切面的程式設計,許多不容易用傳統OOP實現的功能可以通過AOP輕鬆應付。
3.宣告式事務的支援
在Spring中,我們可以從單調煩悶的事務管理程式碼中解脫出來,通過宣告式方式靈活地進行事務的管理,提高開發效率和質量。
4.方便程式的測試
可以用非容器依賴的程式設計方式進行幾乎所有的測試工作,在Spring裡,測試不再是昂貴的操作,而是隨手可做的事情。例如:Spring對Junit4支援,可以通過註解方便的測試Spring程式。
5.方便整合各種優秀框架
Spring不排斥各種優秀的開源框架,相反,Spring可以降低各種框架的使用難度,Spring提供了對各種優秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支援。
6.降低Java EE API的使用難度
Spring對很多難用的Java EE API(如JDBC,JavaMail,遠端呼叫等)提供了一個薄薄的封裝層,通過Spring的簡易封裝,這些Java EE API的使用難度大為降低。
Spring的組成
Spring Core:Spring 框架的核心。Spring其它元件都依賴於核心元件,主要通過BeanFactory提供IOC等服務。
Spring Context:Sprin上下文是一個配置檔案,向 Spring框架提供上下文資訊。Spring 上下文包括企業服務,例如JNDI、EJB、電子郵件、國際化、校驗和排程功能。
Spring AOP:通過配置管理特性,Spring AOP 模組直接將面向切面的程式設計功能整合到了 Spring 框架中。
Spring ORM:Spring 框架插入了若干個ORM框架,從而提供了 ORM 的物件關係工具,其中包括JDO、Hibernate和iBatisSQL Map。所有這些都遵從 Spring 的通用事務和 DAO 異常層次結構。
Spring DAO: DAO抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不同資料庫供應商丟擲的錯誤訊息。異常層次結構簡化了錯誤處理,並且極大地降低了需要編寫的異常程式碼數量(例如開啟和關閉連線)。Spring DAO 的面向 JDBC 的異常遵從通用的 DAO 異常層次結構。
Spring Web: Web 上下文模組建立在應用程式上下文模組之上,為基於 Web 的應用程式提供了上下文。所以,Spring框架支援與 Jakarta Struts 的整合。Web 模組還簡化了處理多部分請求以及將請求引數繫結到域物件的工作。
Spring Web MVC: 為 web 應用提供了模型檢視控制(MVC)和 REST Web 服務的實現。Spring 的 MVC 框架可以使領域模型程式碼和 web 表單完全地分離,且可以與 Spring 框架的其它所有功能進行整合。
IOC和DI
IOC(Inverse Of Control)是控制反轉的意思,作用是降低物件之間的耦合度。
一般情況下我們直接在物件內部通過new進行建立物件,是程式主動去建立依賴物件;而IoC是有專門一個容器來建立這些物件,即由Ioc容器來控制物件的建立;這樣就是由容器來控制物件,而不是由我們的物件來控制,這樣就完成了控制反轉。
DI(Dependency Injection)即“依賴注入”:是元件之間依賴關係由容器在執行期決定,形象的說,即由容器動態的將某個依賴關係注入到元件之中。依賴注入的目的並非為軟體系統帶來更多功能,而是為了提升元件重用的頻率,併為系統搭建一個靈活、可擴充套件的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何程式碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。
IOC的原理:註解+反射
下面的案例講解了IOC的原理,模擬為電腦配置不同的CPU和記憶體,CPU有AMD和INTEL兩種,記憶體有DDR8G和DDR16G兩種
- /**
- * CPU介面
- */
- public interface Cpu {
- void run();
- }
- /**
- * 記憶體介面
- */
- public interface Memory {
- void read();
- void write();
- }
- /**
- * AMD的CPU
- */
- public class AMDCpu implements Cpu {
- public void run() {
- System.out.println("AMD的CPU正在執行....");
- }
- }
- /**
- * Intel的CPU
- */
- public class IntelCpu implements Cpu{
- public void run() {
- System.out.println("Intel的CPU正在執行....");
- }
- }
- /**
- * DDR8G的記憶體
- */
- public class DDR8GMemory implements Memory {
- public void read() {
- System.out.println("使用DDR8G的記憶體讀取資料....");
- }
- public void write() {
- System.out.println("使用DDR8G的記憶體寫入資料....");
- }
- }
- /**
- * DDR16G的記憶體
- */
- public class DDR16GMemory implements Memory {
- public void read() {
- System.out.println("使用DDR16G的記憶體讀取資料....");
- }
- public void write() {
- System.out.println("使用DDR16G的記憶體寫入資料....");
- }
- }
- public class TestComputer {
- @Test
- public void testComputer(){
- //硬編碼方式建立物件
- Computer computer = new Computer();
- Cpu cpu = new IntelCpu();
- Memory memory = new DDR16GMemory();
- computer.setCpu(cpu);
- computer.setMemory(memory);
- computer.start();
- }
- }
上面是使用硬編碼方式建立電腦的CPU和記憶體屬性,程式碼和具體的子類緊密耦合,不利於後期的維護和擴充套件。
修改的思路是:不由讓程式主動建立去建立CPU和記憶體物件,而是通過註解方式標記CPU和記憶體的型別,使用反射將CPU和記憶體的物件注入到電腦的屬性中。
新增程式碼:
- /**
- * 電腦元件的註解
- */
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- public @interface MyComponent {
- /**
- * 元件型別
- * @return
- */
- Class componentClass();
- }
- /**
- * 電腦類
- */
- public class Computer {
- @MyComponent(componentClass = IntelCpu.class)
- private Cpu cpu;
- @MyComponent(componentClass = DDR8GMemory.class)
- private Memory memory;
- ....}
- public class TestComputer {
- @Test
- public void testComputer(){
- //通過反射和註解,將cpu和memory屬性注入進去
- try {
- //獲得Computer型別
- Class<Computer> computerClass = Computer.class;
- //建立Computer物件
- Computer computer = computerClass.newInstance();
- //獲得Computer物件的屬性
- Field[] fields = computerClass.getDeclaredFields();
- //遍歷屬性
- for(Field field : fields){
- //獲得屬性上定義的MyComponent註解
- MyComponent anno = field.getDeclaredAnnotation(MyComponent.class);
- //獲得配置的元件型別
- Class aClass = anno.componentClass();
- //建立該元件的物件
- Object comp = aClass.newInstance();
- //呼叫set方法賦值給屬性
- String name = field.getName();
- name = "set" + name.substring(0,1).toUpperCase() + name.substring(1);
- //通過方法名和引數型別獲得方法
- Method method = computerClass.getDeclaredMethod(name, field.getType());
- //呼叫方法
- method.invoke(computer,comp);
- }
- //啟動電腦
- computer.start();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
程式如上面修改後,後期如果需要修改電腦的配置,只需要修改註解配置的型別,就可以注入不同的電腦元件,這樣就降低了程式碼間的耦合性,維護程式碼變得比較簡單。
@MyComponent(componentClass = AMDCpu.class)
private Cpu cpu;
@MyComponent(componentClass = DDR16GMemory.class)
private Memory memory;
總結
IOC(控制反轉)是Spring最重要的原理,它將建立物件的主動權交給Spring容器,Spring程式只需要進行一些配置,就可以使用不同的物件,極大的降低了程式碼耦合性,提高了程式的靈活性,IOC的實現原