【Spring入門系列】代理模式和AOP
代理模式概述
代理是一種設計模式,提供了對目標物件另外的訪問方式,通過代理物件訪問目標物件。這樣可以在目標物件功能實現的基礎上,增加額外的功能,從而達到擴充套件目標物件功能的效果。簡言之,代理模式就是設定一箇中間代理來控制訪問原目標物件,以達到增強原物件的功能和簡化訪問方式。
靜態代理
這種代理方式需要代理物件和目標物件實現一樣的介面。
優點:可以在不修改目標物件的前提下擴充套件目標物件的功能。
缺點:1、冗餘,由於代理物件要實現與目標物件一致的介面,會產生過多的代理類。2、不易維護,一旦介面增加方法,目標物件與代理物件都要進行修改。
/**
* 介面類
*/
public interface IUserService {
void save();
void update();
}
/**
* 目標物件類
*/
public class Userservlce implements IUserService {
@Override
public void save() {
System.out.println("儲存資料成功");
}
@Override
public void update() {
System.out.println("更新資料成功");
}
/**
* 代理物件類
*/
public class UserServiceProxy implements IUserService {
private IUserService target;
public UserServiceProxy(IUserService target){
this.target = target;
}
@Override
public void save() {
System.out.println("開啟事務");
target.save();
System.out.println("提交事務");
}
@Override
public void update() {
System.out.println("開啟事務");
target.update();
System.out.println("提交事務");
}
/**
* 測試類 */ public class TestProxy { @Test public void testStaticProxy(){ IUserService target = new Userservlce(); IUserService proxy = new UserServiceProxy(target); proxy.save();
proxy.update();
}
}
動態代理
動態地在記憶體中構建代理物件,從而實現對目標物件的代理功能,動態代理又被稱為JDK代理或介面代理。
動態代理物件不需要實現介面,但是要求目標物件必須實現介面,否則不能使用動態代理。
靜態代理與動態代理的主要區別:
靜態代理在編譯時就已經實現,編譯完成後代理類是一個實際的class檔案。
動態代理是在執行時動態生成的,即編譯完成後沒有實際的class檔案,而是在執行時動態生成類位元組碼,並載入到JVM中。
Proxy這個類的作用就是用來動態建立一個代理物件的類,它提供了許多的方法,但是用的最多的就是 newProxyInstance 這個方法。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handle) throws IllegalArgumentException
這個方法的作用就是得到一個動態的代理物件,其接收三個引數,loader:一個ClassLoader物件,定義了由哪個ClassLoader來對生成的代理物件進行載入;interfaces:一個Interface物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了;handle:一個InvocationHandler物件,表示當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上。
其實我們所說的動態代理類是這樣一種class:它是在執行時生成的class,在生成它時你必須提供一組interface給它,然後該class就宣稱它實現了這些interface。這樣一來,我們可以把該class的例項當作這些interface中的任何一個來用(可以強轉為相應的介面型別)。當然,這個動態代理類其實就是一個Proxy,它不會做實質性的工作,在生成它的例項時你必須提供一個handler,由它接管實際的工作。
1 public class ProxyFactory { 2 3 private Object target; 4 5 public ProxyFactory(Object target){ 6 this.target = target; 7 } 8 9 public Object getProxyInstance(){ 10 11 return Proxy.newProxyInstance( 12 target.getClass().getClassLoader(), 13 target.getClass().getInterfaces(), 14 new InvocationHandler() { 15 @Override 16 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 17 System.out.println("開啟事務"); 18 19 // 執行目標物件方法 20 Object returnValue = method.invoke(target, args); 21 22 System.out.println("提交事務"); 23 return returnValue; 24 } 25 }); 26 } 27 }
1 /** 2 * 測試類 3 */ 4 public class TestProxy { 5 6 @Test 7 public void testDynamicProxy (){ 8 IUserService target = new Userservlce(); 9 // 輸出目標物件資訊 10 System.out.println(target.getClass()); 11 // 獲取代理物件例項 12 IUserService proxy = (IUserService) new ProxyFactory(target).getProxyInstance(); 13 // 輸出代理物件資訊 14 System.out.println(proxy.getClass()); 15 // 呼叫代理物件方法,觸發事件處理器 16 proxy.save(); 17 proxy.update(); 18 } 19 }