1. 程式人生 > >JAVA設計模式——代理(動態代理)

JAVA設計模式——代理(動態代理)

生成 底層 exe 加載器 use 有一個 隨筆 for oca

  傳送門:JAVA設計模式——代理(靜態代理)

  序言:

  在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴註入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麽通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麽樣的一個原理,而AOP的原理就是java的動態代理機制,所以本篇隨筆就是對java的動態機制進行一個回顧。

動態代理模式主要由四個元素共同構成:

  1. 接口,接口中的方法是要真正去實現的

  2. 被代理類,實現上述接口,這是真正去執行接口中方法的類

  3. InvocationHandler接口的實現,幫助被代理類去實現方法

  4. 代理類(Proxy)

一、InvocationHandler

在java的動態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和接口是實現我們動態代理所必須用到的。

每一個動態代理類都必須要實現InvocationHandler這個接口,並且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個接口的 invoke 方法來進行調用。

我們來看看InvocationHandler這個接口的唯一一個方法 invoke

方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我們看到這個方法一共接受三個參數,那麽這三個參數分別代表什麽呢?

  • proxy: 指代我們所代理的那個真實對象
  • method:  指代的是我們所要調用真實對象的某個方法的Method對象
  • args:  指代的是調用真實對象某個方法時接受的參數

如果不是很明白,等下通過一個實例會對這幾個參數進行更深的講解。

二、Proxy類:

Proxy這個類的作用就是用來動態創建一個代理對象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

這個方法的作用就是得到一個動態的代理對象,其接收三個參數,我們來看看這三個參數所代表的含義:

  • loader:  一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載
  • interfaces:  一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麽接口,如果我提供了一組接口給它,那麽這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了
  • h:  一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上

三、例子:

動態代理:就是實現階段不用關系代理是哪個,而在運行階段指定具體哪個代理。

抽象接口的類圖如下:

技術分享圖片

      --圖來自設計模式之禪

所以動態代理模式要有一個InvocationHandler接口 和 InvocationHandler的實現類GamePlayerIH。其中 InvocationHandler是JD提供的動態代理接口,對被代理類的方法進行代理。

代碼實現如下

抽象主題類或者接口:

package com.yemaozi.proxy.dynamic;

/*
 * 動態代理:就是實現階段不用關系代理是哪個,而在運行階段指定具體哪個代理。
 */
public interface IGamePlayer {
    //登錄遊戲
    public void login(String username, String password);
    
    //擊殺Boss
    public void killBoss();
    
    //升級
    public void upGrade();
}

被代理類:

package com.yemaozi.proxy.dynamic;

public class GamePlayer implements IGamePlayer {
    
    private String name = "";
    
    public GamePlayer(String name){
        this.name = name;
    }
    
    public void login(String username, String password) {
        System.out.println("登錄名為 "+username+" 進入遊戲," + name + " 登錄        成功!");
    }
        
    public void killBoss() {
        System.out.println(this.name + " 擊殺了Boss!");
    }

    public void upGrade() {
        System.out.println(this.name + "升級了!");
    }

}    

動態代理處理器類:

package com.yemaozi.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class GamePlayerIHr implements InvocationHandler{

    //被代理的對象
    private Object obj;
    
    //將需要代理的實例通過處理器類的構造方法傳遞給代理。
    public GamePlayerIHr (Object obj){
        this.obj = obj;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = null;
        if("login".equalsIgnoreCase(method.getName())){
            //這個方法不受任何影響的情況下,在方法前後添加新的功能,或者增強方法,
            //從側面切入從而達到擴展的效果的編程,就是面向切面編程(AOP Aspect Oriented Programming)。
            //AOP並不是新技術,而是相對於面向對象編程的一種新的編程思想。在日誌,事務,權限等方面使用較多。
            System.out.println("代理登錄遊戲!");
            result = method.invoke(this.obj, args);
            return result;
        }
        result = method.invoke(this.obj, args);
        return result;
    }

}

由於代理是動態產生的,所以不需要再聲明代理類。

動態代理場景類:

package com.yemaozi.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        IGamePlayer gp = new GamePlayer("張三");
        InvocationHandler gpHandler = new GamePlayerIH(gp);
        //獲取主題類的類加載器ClassLoader
        ClassLoader classLoader = gp.getClass().getClassLoader();
        //一個Interface對象的數組
        Class<?>[] cls = new Class[]{IGamePlayer.class};
        //動態產生一個代理者。
        IGamePlayer proxyGp =(IGamePlayer)Proxy.newProxyInstance(classLoader, cls, gpHandler);
        proxyGp.login("zhangsan", "123456");
        proxyGp.killBoss();
        proxyGp.upGrade();
    }
}

執行結果:
代理登錄遊戲!
登錄名為 zhangsan 進入遊戲,張三 登錄成功!
張三 擊殺了Boss!
張三升級了!

//在此,我們沒有創建代理類,但是確實有代理類幫我們完成事情。    

四、動態代理的應用———AOP

在此代理模式中,不僅代理是動態產生的(即在運行的時候生成),而且還在代理的時候,也增加了一些處理。在此處增加的處理,其實就是另一種編程思想-----面向切面編程思想(AOP Aspect Oriented Programming)。

帶有AOP的動態代理模式類圖:

技術分享圖片

        --圖來自設計模式之禪

從上圖中,可以看出有兩個相對獨立的模塊(Subject和InvocationHandler)。動態代理實現代理的職責,業務邏輯Subject實現相關的邏輯功能,兩者之間沒有必然的相互耦合的關系。然而,通知Advice從另一個切面切入,最終在上層模塊就是Client耦合,完成邏輯的封裝。

代碼清單如下

抽象主題或者接口:

package com.yemaozi.proxy.dynamic_aop;

public interface Subject {
    public void doSomething(String str);
    //...可以多個邏輯處理方法。。。
}

主題的實現類:

package com.yemaozi.proxy.dynamic_aop;

public class RealSubject implements Subject{

    public void doSomething(String str) {
        //do something...
        System.out.println("do something..." + str);
    }

}

AOP的通知接口:

package com.yemaozi.proxy.dynamic_aop;

//通知接口及定義、
public interface IAdvice {
    public void exec();
}

前置通知

package com.yemaozi.proxy.dynamic_aop;

public class BeforeAdvice implements IAdvice {
    //在被代理的方法前來執行,從而達到擴展功能。
    public void exec() {
        System.out.println("前置通知被執行!");
    }
}

後置通知:

package com.yemaozi.proxy.dynamic_aop;

public class AfterAdvice implements IAdvice {
    
    //在被代理的方法後來執行,從而達到擴展功能。
    public void exec() {
        System.out.println("後置通知被執行!");
    }
}

動態代理的處理器類:

所有的方法通過invoke方法類實現。

package com.yemaozi.proxy.dynamic_aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    //被代理的對象
    private Subject realSubject;
    //通過MyInvocationHandler的構造方法將被代理對象傳遞過來。
    public MyInvocationHandler(Subject realSubject){
        this.realSubject = realSubject;
    }
    //執行被代理類的方法。
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在執行方法前,執行前置通知。
        IAdvice beforeAdvice = new BeforeAdvice();
        beforeAdvice.exec();
        Object result = method.invoke(this.realSubject, args);
        //在執行方法後,執行後置通知。
        IAdvice afterAdvice = new AfterAdvice();
        afterAdvice.exec();
        //前置通知,和後置通知,都是要看具體實際的業務需求來進行添加。
        return result;
    }

}

動態代理類(Proxy)

package com.yemaozi.proxy.dynamic_aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class DynamicProxy {
    
    /**
     * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler handler)
     * loader:
     *             一個ClassLoader對象,定義了由哪個ClassLoader對象,來對生產的代理進行加載。
     * interfaces:
     *             一個Interfaces數組,表示我將要給我所代理的對象提供一組什麽樣的接口,
     *             如果提供一組接口給它,那麽該代理對象就宣稱實現了該接口,從而可以調用接口中的方法。
     *             即,查找出真是主題類的所實現的所有的接口。
     * handler:
     *             一個InvocationHandler對象,表示當我這個動態代理對象在調用方法時,會關聯到該InvocationHandler對象。
     *             該InvocationHandler與主題類有著關聯。
     */
    public static <T> T newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler handler){
        @SuppressWarnings("unchecked")
        T t = (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
        return t;
    }
}

動態代理場景類:

package com.yemaozi.proxy.dynamic_aop;

import java.lang.reflect.InvocationHandler;

public class AOPClient {
    
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        InvocationHandler handler = new MyInvocationHandler(realSubject); 
        ClassLoader classLoader = realSubject.getClass().getClassLoader();
        Class<?>[] interfaces = realSubject.getClass().getInterfaces();
        Subject proxySubect = DynamicProxy.newProxyInstance(classLoader, interfaces, handler);
        proxySubect.doSomething("這是一個Dynamic AOP示例!!!");
    }
}

執行結果:
前置通知被執行!
do something...這是一個Dynamic AOP示例!!!
後置通知被執行!

動態代理中invoke的動態調用:

技術分享圖片

動態代理類DynamicProxy是個純粹的動態創建代理類通用類。

所以在具體業務中,可以在進一步封裝具體的具有業務邏輯意義的DynamicProxy類。

代碼如下

具體業務的動態代理:

package com.yemaozi.proxy.dynamic_aop;

import java.lang.reflect.InvocationHandler;
//具體業務的動態代理。
public class SubjectDynamicProxy extends DynamicProxy {
    public static <T> T newProxyInstance(Subject subject){
        ClassLoader classLoader = subject.getClass().getClassLoader();
        Class<?>[] interfaces = subject.getClass().getInterfaces();
        InvocationHandler handler = new MyInvocationHandler(subject);
        T t = newProxyInstance(classLoader, interfaces, handler);
        return t;
    }
}

動態代理在現在用的是非常的多的,如像Spring AOP ,DBCP連接池,AspectJ等

JAVA設計模式——代理(動態代理)