1. 程式人生 > >【Spring入門系列】代理模式和AOP

【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 }