Spring核心技術學習總結
這段時間,看了一些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()的功能
- }
interface Ham
{
void show();//由Ham"加工裝置"生產出的各種Ham將有show()的功能
}
B、"工廠":即包含了"車間"、加工所需的"原料"、"加工裝置"、最終加工的"產品"等實體的複合型實體。在工廠模式中,"工廠"可以用一個類來表示,"車間"可以用該類中的函式來表示,"原料"可以用該函式中的實參來表示,最終的"產品"可以用該函式的返回值來表示。在這裡,"車間"遠端呼叫了"加工裝置"(即每種"產品"的建構函式)。下面是"工廠"類的具體實現程式碼:
- publicclass FatoryModule
- //"工廠"類,用於生產不同種類的Ham
- {
- public Ham getHam(String HamType) throws Exception
- //"工廠"中加工Ham的"車間";HamType為該"車間"中的"原料"
- {
- if (HamType.equals("HamA"))
- {
- returnnew HamA();//根據不同的"原料"得到不同的"產品",下同
- }
- if (HamType.equals("HamB"
- {
- returnnew HamB();
- }
- if (HamType.equals("HamC"))
- { 字串8
- returnnew HamC();
- }
- else
- thrownew Exception();
- }
- publicstaticvoid main(String[] args)
- //測試函式
- {
- FatoryModule fatorymodule = new FatoryModule();
- try
- {
- Ham myHam = fatorymodule.getHam(args[0]);
- myHam.show();//實際上呼叫的是args[0]所對應某類具體產品的show()
- }
- catch (Exception ex)
- {
- ex.printStackTrace();
- }
- }
- }
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
- {
- publicvoid show()
- {
- System.out.println("You got a HamA.");
- }
- }
- class HamB implements Ham
- // "產品"HamB
- {
- publicvoid show()
- {
- System.out.println("You got a HamB.");
- }
- }
- class HamC implements Ham
- // "產品"HamC
- {
- publicvoid show()
- {
- System.out.println("You got a HamC.");
- }
- }
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 介面注入
該方式利用介面將呼叫元件和實現元件相分離。請看下面的程式碼:
- publicclass ClassA
- {
- private InterfaceB clzB;
- public Object doSomething(InterfaceB b)
- {
- clzB = b;
- return clzB.doIt();
- }
- ………
- }
public class ClassA
{
private InterfaceB clzB;
public Object doSomething(InterfaceB b)
{
clzB = b;
return clzB.doIt();
}
………
}
上面的實參b的值由容器(spring中的相關配置檔案)定義,並在執行期間由該容器提供。相關配置檔案的內容我們以後會專門加以介紹。
1.2.2 設值注入
設值注入是spring中應用最廣泛的DI模式,與其它模式相比,該模式更為直觀、自然。該模式主要通過類的setter方法來完成依賴關係的設定。請看下面的程式碼:
- publicclass DIByConstructor
- {
- privatefinal DataSource dataSource;
- privatefinal String message;
- ………
- public setDataSource(DataSource ds)
- {
- this.dataSource = ds;
- }
- public setmessage(String msg)
- {
- this.message = msg;
- }
- public getDataSource()
- {
- returnthis.dataSource;
- }
- public getmessage()
- {
- returnthis.message;
- }
- ………
- }
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 構造子注入
構造子注入,即通過建構函式完成依賴關係的設定,請看下面的程式碼:
- publicclass DIByConstructor
- {
- privatefinal DataSource dataSource;
- privatefinal String message;
- public DIByConstructor(DataSource ds, String msg)
- {
- this.dataSource = ds;
- this.message = msg;
- }
- }
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;
- publicclass A //"目標物件"A,AOP框架將自動為該物件建立一個"代理物件"
- {
- publicvoid sayHello()
- {
- System.out.println("Hello, I'm a");
- }
- };
package com.longthsoft.learn.spring.models;
public class A //"目標物件"A,AOP框架將自動為該物件建立一個"代理物件"
{
public void sayHello()
{
System.out.println("Hello, I'm a");
}
};
Java程式碼
- package com.longthsoft.learn.spring.models;
- publicclass B //"目標物件"B,AOP框架將自動為該物件建立一個"代理物件"
- {
- publicvoid sayHi()
- {
- System.out.println("Hi, I'm b"); 字串1
- }
- }
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語法建立一個"切面"
- publicclass SimpleAspect
- {
- @Pointcut("execution(* com.longthsoft.learn.spring.models.*.say*())")
- //建立"切入點",該"切入點"捕獲了指定專案中相關類的方法("連線點")
- publicvoid simplePointcut() { }
- @AfterReturning(pointcut="simplePointcut()")
- 字串4
- //建立"通知",該"通知"綁定了具體的"切入點"
- publicvoid simpleAdvice() //"切入點"的執行程式碼,將被"目標物件"的"代理物件"執行
- {
- System.out.println("Merry Christmas");
- }
- }
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>
<?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;
- publicfinalclass Boot
- {
- publicstaticvoid 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();
- }
- }
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技術基礎)》