這段時間,看了一些Spring文檔和資料,對其有了一個基本的了解。Spring的核心技術由兩大部分組成:IoC和AOP,下面我們就分别對它們進行介紹。
1 IoC技術
1.1 預備知識
IoC即Inversion of Control(控制反轉)的簡寫,它是一種設計模式,Spring隻不過是實作了該模式。IoC是工廠模式的升華,同時涉及到了反射的概念。是以,在正式介紹IoC之前,首先介紹一下幾個基本的概念及技術:接口、工廠模式、反射。
1.1.1 接口
作為面向對象的語言,和C++不同的是,JAVA不支援多重繼承,即一個子類隻能繼承自一個父類,像Son extends FatherA,FatherB 是錯誤的。于是産生了接口這個概念,即JAVA可以實作多個接口,比如:Son extends FatherA implements FatherB, FatherC是允許的。接口的主要特征包括:
A、其中的方法均沒有實體(即隻聲名未實作),就這一點而言就相當于abstact class,如:
interface ITF
{
void func(int i);
}
上例中,ITF是一個接口,它僅僅聲明了一個方法func,而沒有具體實作它。
B、一個類欲實作某接口,則必須實作該接口内的所有方法。例如: 字串5
class aclass implements ITF
{
public void func (int i)
{
//在這裡你可以不作任何處理,但是必須實作該方法
}
}
C、一個類可以實作多個接口。
D、接口沒有執行個體變量。
E、接口中所有變量都必須被定義為final static。
F、接口可以繼承多個接口。
以上隻是接口的幾個典型特征,與其相關的内容還有很多,如果您想對其有更深的了解,請通路其他相關資源。
1.1.2 工廠模式
工廠模式是最常用的設計模式之一(我對所謂的設計模式沒有仔細研究過,該模式是我看到次數最多的一個,是以才這麼說,呵呵)。今天看了一些例子,覺得工廠模式這個名字起得相當有創意,這是因為在該模式中,"工廠"、"工廠中的房間"、"原料"、"加工裝置"、"原型産品"、"産品"等概念樣樣俱全。下面,我們在一個經典例子的基礎上,引用上述概念,對工廠模式進行較為形象的解釋。
現在我們模拟一個火腿(Ham)加工廠,該工廠可以生産若幹種類型的Ham,在該廠中,上述概念可依次描述如下:
字串8
A、"原型産品":即"産品"的模型,該模型定義(但未實作)了各類"産品"必須要具備的功能,各類"産品"對應的"加工裝置"可根據該"産品"的特點實作這些功能。是以,在工廠模式中,"原型産品"可以用一個接口來表示:
interface Ham
{
void show();//由Ham"加工裝置"生産出的各種Ham将有show()的功能
}
B、"工廠":即包含了"工廠中的房間"、加工所需的"原料"、"加工裝置"、最終加工的"産品"等實體的複合型實體。在工廠模式中,"工廠"可以用一個類來表示,"工廠中的房間"可以用該類中的函數來表示,"原料"可以用該函數中的實參來表示,最終的"産品"可以用該函數的傳回值來表示。在這裡,"工廠中的房間"遠端調用了"加工裝置"(即每種"産品"的構造函數)。下面是"工廠"類的具體實作代碼:
public class FatoryModule
//"工廠"類,用于生産不同種類的Ham
{
public Ham getHam(String HamType) throws Exception
//"工廠"中加工Ham的"工廠中的房間";HamType為該"工廠中的房間"中的"原料"
{
if (HamType.equals("HamA"))
{
return new HamA();//根據不同的"原料"得到不同的"産品",下同
}
if (HamType.equals("HamB"))
{
return new HamB();
}
if (HamType.equals("HamC"))
{ 字串8
return new HamC();
}
else
throw new Exception();
}
public static void main(String[] args)
//測試函數
{
FatoryModule fatorymodule = new FatoryModule();
try
{
Ham myHam = fatorymodule.getHam(args[0]);
myHam.show();//實際上調用的是args[0]所對應某類具體産品的show()
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
C、"産品":即"原型産品"的模型實作實體。"工廠中的房間"根據具體的"原料"調用對應的"加工裝置"來實作"原型産品"定義的所有功能,最後得到所需的"産品"。是以,"産品"可以用一個類來表示,具體代碼如下:
class HamA implements Ham
// "産品"HamA
{
public void show()
{
System.out.println("You got a HamA.");
}
}
class HamB implements Ham
// "産品"HamB
{
public void show()
{
System.out.println("You got a HamB.");
}
}
class HamC implements Ham
// "産品"HamC
{
public void show()
{
System.out.println("You got a HamC.");
}
}
小結:總體感覺工廠模式是接口的典型應用。該模式最大特點是友善我們"加工"指定參數條件下的産品。以上述情況為例,假如我們不采用該模式的話,要得到一種産品就必須new一個該産品類的執行個體,而且在程式中不同産品類的執行個體變量不能采用同一個變量名。假如有一天我們想更換某一類産品的名字,考慮到程式的可讀性,該類産品的執行個體變量名也需要修改,這個工作量也需會很大(所有涉及到該變量名的地方都需要修改)。從這一點來講,工廠模式能夠減輕我們代碼維護量。
1.1.3 反射(主要應用于Spring的四種Bean封裝機制中的Bean Wrapper機制)
反射是java的一種非常有趣且有用的特征,而且也是Java被視為動态(或準動态)語言的一個非常關鍵的性質。該機制允許java程式在運作時通過Reflection APIs(java.lang.reflect.*)取得任何一個已知名稱的class的内部資訊,包括其modifiers(諸如public, static 等等)、superclass(如Object等)、所實作的interfaces(如Cloneable等),以及fields和methods的所有資訊,并可在程式運作時改變fields的内容或喚起methods。其實,反射的最重要作用就是允許我們通過類(方法)名調用類(方法)。
更具體的内容請看候捷先生的文章:"Java反射機制",具體網址為:http://www.j2medev.com/Article/Class3/Class7/200604/1995.html。如果您想在短時間内對java的反射機制有一個大體的了解,請看一個網友的一篇部落格:"java的reflect機制(反射)",網址為:http://jackleliu.spaces.live.com/blog/cns!426307897fa1054e!125.entry。
1.2 正式進入IoC
IoC實際上是一個高層次的概念,是一種思想或者設計模式,Spring等J2EE架構或者技術隻不過是該思想的實作産品。簡單的來講,IoC要求我們利用容器,而不是我們自己編寫的代碼去控制系統中應用程式之間的關系。即對應用程式之間的關系由原來的手工編碼控制轉換為利用容器控制。是以,我們把這種思想叫做控制反轉(IoC)。
IoC有多種實作方法,其中,Spring是通過一種名為DI(Dependency Injection,即依賴注入)的方法實作的。用夏昕先生的話來講,"所謂依賴注入,即元件之間的依賴關系由容器在運作期決定,形象的來說,即由容器動态的将某種依賴關系注入到元件之中。"在Spring中,上面所說的容器一般是一種配置檔案(.Xml等類型的檔案)。
通過使用DI,當元件之間關系發生變化時,我們隻需要修改系統中相關的配置檔案,不需要改動我們的代碼,這既減少了我們的程式維護量,又提高了我們程式的可重用性。
DI主要有三種實作方式:接口注入、設值注入、構造子注入,下面分别加以介紹:
1.2.1 接口注入
該方式利用接口将調用元件和實作元件相分離。請看下面的代碼:
public class ClassA
{
private InterfaceB clzB;
public Object doSomething(InterfaceB b)
{
clzB = b;
return clzB.doIt();
}
………
}
上面的實參b的值由容器(spring中的相關配置檔案)定義,并在運作期間由該容器提供。相關配置檔案的内容我們以後會專門加以介紹。
1.2.2 設值注入
設值注入是spring中應用最廣泛的DI模式,與其它模式相比,該模式更為直覺、自然。該模式主要通過類的setter方法來完成依賴關系的設定。請看下面的代碼:
public class DIByConstructor
{
private final DataSource dataSource;
private final String message;
………
public setDataSource(DataSource ds)
{
this.dataSource = ds;
}
public setmessage(String msg)
{
this.message = msg;
}
public getDataSource()
{
return this.dataSource;
}
public getmessage()
{
return this.message;
}
………
}
上面的實參ds和msg的值由容器(spring中的相關配置檔案)定義,并在運作期間由該容器提供。相關配置檔案的内容我們以後會專門加以介紹。
1.2.3 構造子注入
構造子注入,即通過構造函數完成依賴關系的設定,請看下面的代碼:
public class DIByConstructor
{
private final DataSource dataSource;
private final String message;
public DIByConstructor(DataSource ds, String msg)
{
this.dataSource = ds;
this.message = msg;
}
}
可以看到,在該模式中,依賴關系是通過類構造函數建立的,容器通過調用類的構造方法,将其所需的依賴關系注入其中。其中,構造函數的實參ds和msg的值由容器(spring中的相關配置檔案)定義,并在運作期間由該容器提供。相關配置檔案的内容我們以後會專門加以介紹。
1.2.4 幾種注入模式的比較
個人覺得,這部分屬于理論研究的範疇,是以在此不予介紹。如果您對此感興趣的話,請參考相關資料。
2 AOP技術
2.1 AOP基本概念
AOP(Aspect-Oriented Programming,面向切面程式設計)是一種程式設計思想,該思想的主要目标是将系統分為兩部分:一部分封裝了系統中各元件的通用功能(羅輯或者責任),形成一個切面,該切面被稱為應用系統的"橫切關注點",此部分包含了與系統核心商業邏輯關系不大的部分;系統的其餘部分,即核心商業邏輯部分,被稱為系統的"核心關注點",此部分包含了系統中業務處理的主要流程。其中,"橫切關注點"經常出現在"核心關注點"的周圍,但無論出現在何處,它們的基本功能是相似的,比如權限認證、日志、事務處理等。應用中采用這種思想的好處在于:一方面可以使我們在系統開發過程中的責任分工更為明确(比如,可以讓進階工程師負責"橫切關注點"的開發,初級工程師負責"核心關注點"的開發,配置工程師負責将以上兩部分搭建成一個完整的應用系統);另一方面,清晰的系統結構将使我們以後的系統維護工作變得更為輕松。
字串3
下面是AOP的幾個基本概念(了解它們極為重要):
2.1.1 Join point(連接配接點):是程式執行中的一個精确執行點,例如類中的一個方法。它是一個抽象的概念,在實作AOP時,并不需要去定義一個Join point。
2.1.2 Point cut(切入點):本質上是一個捕獲連接配接點的結構(或稱連接配接點的集合)。在AOP中,可以定義一個Point cut,來捕獲相關方法(通知中的邏輯)的調用。
2.1.3 Advice(通知):Point cut的執行代碼,它是執行"切面"的具體邏輯。
2.1.4 Aspect(切面):Point cut和Advice的組合,它類似于OOP中定義的一個類,但它代表的更多的是對象間橫向的關系。
2.1.5 Introduce(引入):為對象引入附加的方法或屬性,進而達到修改對象結構的目的(此概念不是很了解)。
2.1.6 Target Object(目标對象):包含Join point的對象,它是被Advice的類,也是商務邏輯對象。這個對象永遠是一個被代理的對象。
2.1.7 AOP Proxy(AOP代理):由AOP架構建立的對象,它是真正執行Advice的實體。
2.1.8 Weaving(織入):将Aspec子產品與核心商務邏輯(即目标對象)進行關聯的過程,比如,将事務處理子產品和銀行櫃員機程式通過配置結合起來,決定什麼情況下事務處理子產品被通知調用。 字串3
2.2 AOP in Spring2.0應用程式開發基本步驟
在2.0版本以前,Spring所提供的内置AOP支援是基于Java Dynamic Proxy和CGLib實作的,是一種動态的AOP機制。Spring 2.0版本之後,AOP的使用變得更簡單,同時功能也更為強大。通過與AspectJ 5的整合, Spring 2.0提供了更完整的AOP。在Spring 2.0中,AOP主要有三種實作方式:基于AspectJ語言的實作方式;基于模式(schema-based)的實作方式;基于 Dynamic Proxy和CGLib(即同以前版本的Spring是相容的)的實作方式。
下面是一個用Spring2.0的AOP所做的一個例子,該例采用了上面所說的第一種實作方式。限于篇幅,有關AspectJ方面的内容這裡不再叙述,您可以參考相關的資料以對其有一個基本的了解。另外,可以通過MyEclipse最新版本實作該例,該版本提供了對Spring2.0的全面支援。
Step1 首先建立一個普通的Java項目:com.longthsoft.learn.spring
Step2 然後編寫兩個簡單的類,代碼如下:
package com.longthsoft.learn.spring.models;
public class A //"目标對象"A,AOP架構将自動為該對象建立一個"代理對象"
{
public void sayHello()
{
System.out.println("Hello, I'm a");
}
};
package com.longthsoft.learn.spring.models;
public class B //"目标對象"B,AOP架構将自動為該對象建立一個"代理對象"
{
public void sayHi()
{
System.out.println("Hi, I'm b"); 字串1
}
}
Step3 接下來編寫AOP中的"切面"類,代碼如下:
package com.longthsoft.learn.spring;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect //利用AspectJ文法建立一個"切面"
public class SimpleAspect
{
@Pointcut("execution(* com.longthsoft.learn.spring.models.*.say*())")
//建立"切入點",該"切入點"捕獲了指定項目中相關類的方法("連接配接點")
public void simplePointcut() { }
@AfterReturning(pointcut="simplePointcut()")
字串4
//建立"通知",該"通知"綁定了具體的"切入點"
public void simpleAdvice() //"切入點"的執行代碼,将被"目标對象"的"代理對象"執行
{
System.out.println("Merry Christmas");
}
}
Step4 編寫配置檔案(applicationContext.xml),内容如下:
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<aop:aspectj-autoproxy /> //織入"目标對象"和"切面"之間的關聯關系
<bean id="a" class="com.longthsoft.learn.spring.models.A" />
<bean id="b" class="com.longthsoft.learn.spring.models.B" />
<bean id="simpleAspect" class="com.longthsoft.learn.spring.SimpleAspect" />
</beans>
Step5 編寫測試程式,代碼如下:
package com.longthsoft.learn.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.longthsoft.learn.spring.models.A;
import com.longthsoft.learn.spring.models.B;
public final class Boot
{
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
A a = (A) ctx.getBean("a");
a.sayHello();
B b = (B) ctx.getBean("b");
b.sayHi();
}
}
最後運作結果如下:
Hello, I'm a
Merry Christmas
Hi, I'm b
Merry Christmas
3 引用資源
3.1 候捷 《Java反射機制》
3.2 夏昕 《Spring開發指南 V0.8》
3.3 佚名 《AOP技術介紹--(AOP技術基礎)》