跟王老師學反射(十一):動態代理

跟王老師學反射(十一):動態代理

主講教師:王少華   QQ羣號:483773664

學習內容

學會使用動態代理


一、動態代理

動態代理(Dynamic Proxy):相比上一節所實現的靜態代理,動態代理具有更強的靈活性,因爲它不用在我們設計實現的時候就指定某一個代理類來代理哪一個被代理對象,我們可以把這種指定延遲到程序運行時由JVM來實現。

動態代理(Dynamic proxies)是 Java 1.3 引入的特性,在Java的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,通過這個類和接口可以生成JDK動態代理對象

二、Proxy和InvocationHandler

(一)Proxy

1 簡介

Proxy提供用於創建動態代理和代理對象的靜態方法,它也是所有動態代理的父類。如果我們在程序中爲一個或多個接口動態生成實現類,就可以使用Proxy來創建動態代理類;如果需要爲一個或多個接口動態地創建實現,也可以使用Proxy來創建動態代理實現。

2 Proxy提供以下二個方法用於創建動態代理類和動態代理實例

public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):創建一個動態代理類所對應的Class對象,該代理類將實現interfaces所指定的多個接口。第一個ClassLoader指定生成動態代理類的類加載器。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):直接創建一個動態代理對象,該代理對象的實現類實現了interfaces指定的系列接口,執行代理對象的每個方法時都會被替換執行invocationHandler對象的invoke方法

(二)InvocationHandler

InvocationHandler接口是代理處理程序類的實現接口,該接口作爲代理實例的調用處理者的公共父類,每一個代理類的實例都可以提供一個相關的具體調用處理者(InvocationHandler接口的子類)

InvocationHandler接口中有一個方法

1
Object invoke(Object proxy,Method method, Object[] args)

參數:

proxy - 被代理的對象

method - 要調用的方法

args - 方法調用時所需要的參數,如果接口方法不使用參數,則爲 null。基本類型的參數被包裝在適當基本包裝器類(如 java.lang.Integer 或 java.lang.Boolean)的實例中。

返回:

從代理實例的方法調用返回的值。如果接口方法的聲明返回類型是基本類型,則此方法返回的值一定是相應基本包裝對象類的實例;否則,它一定是可分配到聲明返回類型的類型。如果此方法返回的值爲 null 並且接口方法的返回類型是基本類型,則代理實例上的方法調用將拋出 NullPointerException。否則,如果此方法返回的值與上述接口方法的聲明返回類型不兼容,則代理實例上的方法調用將拋出 ClassCastException。

三、動態代理的實現

(一)動態代理的實現步驟

1. 實現InvocationHandler接口創建自己的調用處理器 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class DynmicProxy implements InvocationHandler{
    private Object targetObject;
    public DynmicProxy(){}
    public  DynmicProxy(Object targetObject) {
        this.targetObject = targetObject;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        preHandle();
        Object ret = method.invoke(targetObject, args);
        postHandle();
        return ret;
    }
    /**
     * 代理類特有的方法
     */
    private void preHandle(){
        System.out.println("preHandle");
    }
    /**
     * 代理類特有的方法
     */
    private void postHandle(){
        System.out.println("postHandle");
    }
}

2. 給Proxy類提供ClassLoader和代理接口類型數組創建動態代理類 

1
2
3
4
5
6
7
// 目標對象
        UserService userService = new UserServiceImpl();
        // 創建代理類
        UserService proxyUserService = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new DynmicProxy(userService));

3、通過代理對象調用目標對象方法 

1
2
//通過代理對象調用目標對象的方法
        proxyUserService.save();

(二)代碼優化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class LogHandler implements InvocationHandler{
    //目標對象
    private Object targetObject;
    /**
     * 生成代理對象:就是關聯到哪個接口(與具體的實現類綁定)的哪些方法將被調用時,執行invoke方法。
     * @param targetObject
     * @return 返回代理對象
     */
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        /**
         * 該方法用於爲指定類裝載器、一組接口及調用處理器生成動態代理類實例   
            第一個參數指定產生代理對象的類加載器,需要將其指定爲和目標對象同一個類加載器 
            第二個參數要實現和目標對象一樣的接口,所以只需要拿到目標對象的實現接口 
            第三個參數表明這些被攔截的方法在被攔截時需要執行哪個InvocationHandler的invoke方法
         */
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                    targetObject.getClass().getInterfaces(), this);
    }
    /**
     * proxy:要代理的對象
     * method:要執行的方法
     * args:方法參數
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //原對象方法調用前調用日誌處理
        preHandle();
        //調用目標方法:ret爲目標方法返回值
        Object ret = method.invoke(targetObject, args);
        //原對象方法調用 後調用 日誌處理方法
        postHandle();
        return ret;
    }
     
    /**
     * 代理類特有的方法
     */
    private void preHandle(){
        System.out.println("preHandle");
    }
    /**
     * 代理類特有的方法
     */
    private void postHandle(){
        System.out.println("postHandle");
    }
}
1
2
3
4
5
6
7
public class DynmicTest {
    public static void main(String[] args) {
        LogHandler logHandler = new LogHandler();
        UserService userService = (UserService) logHandler.newProxyInstance(new UserServiceImpl());
        userService.save();
    }
}














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章