java動(dòng)態(tài)代理

原文地址:java動(dòng)態(tài)代理

代理

最近在學(xué)習(xí) Spring 框架,AOP涉及到動(dòng)態(tài)代理的知識(shí),故整理一下,了解動(dòng)態(tài)代理之前,我們首先應(yīng)該了解下什么是代理。

代理是基本的設(shè)計(jì)模式之一,它是你為了提供額外的或不同的操作,而插入的用來(lái)代替的"實(shí)際"對(duì)象的
對(duì)象,這些操作通常涉及與"實(shí)際"對(duì)象的通信,因此代理通常充當(dāng)著中間人的角色。

當(dāng)我們希望跟蹤對(duì)方法的調(diào)用,或者希望度量這些調(diào)用的開(kāi)銷時(shí),我們肯定不希望將這些代碼合并到業(yè)務(wù)代碼中,此時(shí)我們就可以使用代理來(lái)實(shí)現(xiàn)。下面我們通過(guò)一個(gè)示例了解下代理

代碼演示1

定義一個(gè)接口

public interface Behavior {
    void eat();
    void say(String str);
}

接口的實(shí)現(xiàn)類

public class Student implements Behavior {

    @Override
    public void eat() {
        System.out.println("我是學(xué)生,我要吃學(xué)生餐");
    }

    @Override
    public void say(String str) {
        System.out.println(str);
    }
}

定義一個(gè)代理類

public class SimpleProxy implements Behavior {

    private Behavior behavior;

    public SimpleProxy(Student behavior) {
        this.behavior = behavior;
    }

    @Override
    public void eat() {
        System.out.println("---- 方法執(zhí)行前邏輯 ----");
        behavior.eat();
        System.out.println("---- 方法執(zhí)行后邏輯 ----");
    }

    @Override
    public void say(String str) {
        System.out.println("---- 方法執(zhí)行前邏輯 ----");
        behavior.say(str);
        System.out.println("---- 方法執(zhí)行后邏輯 ----");
    }
}

測(cè)試

    @Test
    public void test(){
        SimpleProxy simpleProxy = new SimpleProxy(new Student());
        simpleProxy.eat();
        System.out.println("\n");
        simpleProxy.say("hello,teacher");
    }
    

打印

動(dòng)態(tài)代理

Java的動(dòng)態(tài)代理比代理的思想更進(jìn)一步。因?yàn)樗梢詣?dòng)態(tài)的創(chuàng)建代理并動(dòng)態(tài)的處理對(duì)所代理方法的調(diào)用在動(dòng)
態(tài)代理上所做的所有調(diào)用都會(huì)被重定到單一的調(diào)用處理器上他的工作是揭示調(diào)用的類型并確定相應(yīng)的對(duì)策

實(shí)現(xiàn)Java動(dòng)態(tài)代理的過(guò)程中涉及到一個(gè)接口 InvocationHandler 以及一個(gè)類 Proxy。

接口:InvocationHandler

    InvocationHandler is the interface implemented by the invocation handler 
    of a proxy instance.
    譯:
       InvocationHandler是代理實(shí)例化對(duì)象的調(diào)用處理程序(invocation handler)實(shí)現(xiàn)的接口。
    
    Each proxy instance has an associated invocation handler.When a method is 
    invoked on a proxy instance, the method invocation is encoded and dispatched 
    to the {@code invoke}method of its invocation handler.
    譯:
        每個(gè)代理實(shí)例化對(duì)象都具有一個(gè)關(guān)聯(lián)的調(diào)用處理程序(invocation handler)。當(dāng)代理實(shí)例化對(duì)象
        調(diào)用方法時(shí),將對(duì)方法調(diào)用進(jìn)行編碼并將其指派到它的調(diào)用處理程序的 invoke 方法

InvocationHandler 僅有一個(gè)方法invoke()

      Object invoke(Object proxy,Method method,Object[] args)throws Throwable
           * @param proxy 調(diào)用方法的代理實(shí)例化對(duì)象
           * @param method 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
           * @param args 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)

類:Proxy

    Proxy provides static methods for creating dynamic proxy classes and instances,
    and it is also the superclass of all dynamic proxy classes created by those 
    methods.
    譯:
        Proxy 提供了創(chuàng)建動(dòng)態(tài)代理類和實(shí)例化對(duì)象的方法,同時(shí)也是這些方法創(chuàng)建的所有動(dòng)態(tài)代理類的超類

方法:newProxyInstance

    public static Object newProxyInstance(ClassLoader loader,
                                  Class<?>[] interfaces,
                                  InvocationHandler h)
                           throws IllegalArgumentException
     該方法返回一個(gè)指定接口的動(dòng)態(tài)代理實(shí)例化對(duì)象,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程
     序(invocation handler)
       
     參數(shù):
        loader - 定義代理類的類加載器
        interfaces - 代理類要實(shí)現(xiàn)的接口列表
        h - 指派方法調(diào)用的調(diào)用處理程序

代碼演示2

定義一個(gè)接口

public interface Behavior {
    void eat();
    void say(String str);
}

接口的實(shí)現(xiàn)類

public class Student implements Behavior {

    @Override
    public void eat() {
        System.out.println("我是學(xué)生,我要吃學(xué)生餐");
    }

    @Override
    public void say(String str) {
        System.out.println(str);
    }
}

創(chuàng)建一個(gè)動(dòng)態(tài)代理處理器

/**
 * Created by lizhen on 2018/9/3 下午8:51
 * 定義一個(gè)動(dòng)態(tài)代理處理器,該類必須實(shí)現(xiàn) InvocationHandler 接口
 */
public class DynamicProxyHandler implements InvocationHandler {

    // 目標(biāo)對(duì)象
    private Object target;

    public DynamicProxyHandler(Object target){
        this.target = target;
    }

    /**
     * @param proxy 調(diào)用方法的代理實(shí)例
     * @param method 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
     * @param args 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("------- 方法調(diào)用前操作 ------");
        System.out.println("proxy:" + proxy.getClass());
        System.out.println("Method:" + method);
        method.invoke(target, args);// 當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用
        System.out.println("------- 方法調(diào)用后操作 ------");

        return method.invoke(target, args);
    }
}

測(cè)試

   @Test
    public void test() {

        // 我們要代理的真實(shí)對(duì)象
        Student student = new Student();

        // 我們要代理哪個(gè)真實(shí)對(duì)象,就將該對(duì)象傳進(jìn)去,最后是通過(guò)該真實(shí)對(duì)象來(lái)調(diào)用其方法的
        InvocationHandler handler = new DynamicProxyHandler(student);

        /*
         *  實(shí)例化代理對(duì)象
         * loader:定義代理類的類加載器,一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
         * interfaces: student.getClass().getInterfaces(),我們這里為代理對(duì)象提供的接口是真實(shí)對(duì)象所實(shí)行的接口,表示我要代理的是該真實(shí)對(duì)象,這樣我就能調(diào)用這組接口中的方法了
         * h: 一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
         */
        Behavior proxies = (Behavior) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), handler);
        System.out.println(proxies.getClass().getName());

        proxies.eat();

        System.out.println("\n");
        System.out.println("=========================== 看 我 華 不 華 麗 ================================");
        System.out.println("\n");

        proxies.say("hello,teacher");
    }
    

打印

代碼演示3

   通過(guò)內(nèi)部類定義具體的InvocationHandler

定義接口、實(shí)現(xiàn)類同上

創(chuàng)建 ProxyFactory

public class ProxyFactory {

    // 目標(biāo)對(duì)象
    private Object target;

    public ProxyFactory(Object target){
        this.target = target;
    }

    // 給目標(biāo)對(duì)象生成代理對(duì)象(內(nèi)部類)
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler(){
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("------- 方法調(diào)用前操作 ------");
                        System.out.println("proxy:" + proxy.getClass());
                        if (args != null){
                            for (Object obj: args) {
                                System.out.println("args:" + obj);
                            }
                        }
                        System.out.println("Method:" + method);
                        method.invoke(target, args);// 當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用
                        System.out.println("------- 方法調(diào)用后操作 ------");

                        return null;
                    }
                }
        );
    }
}

測(cè)試

@Test
public void test(){
    Student student = new Student();
    ProxyFactory proxyFactory = new ProxyFactory(student);
    Behavior behavior = (Behavior)proxyFactory.getProxyInstance();
    System.out.println("\n");
    System.out.println(behavior.getClass().getName());
    System.out.println("\n");

    behavior.eat();

    System.out.println("\n");
    System.out.println("=========================== 看 我 華 不 華 麗 ================================");
    System.out.println("\n");

    behavior.say("hello,teacher");
}

打印

Reference

1.java的動(dòng)態(tài)代理機(jī)制詳解 以及文章的評(píng)論使我受益匪淺

2. Java Platform SE 8

3.《Thinking In Java》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自https://blog.csdn.net/briblue/article/details/73928350前...
    扎Zn了老Fe閱讀 460評(píng)論 1 0
  • 原文 代理模式 代理模式是常用的 Java 設(shè)計(jì)模式,它的特征是代理類與委托類有同樣的接口,代理類主要負(fù)責(zé)為委托類...
    Coder_Y閱讀 1,226評(píng)論 0 1
  • 在我們計(jì)統(tǒng)八七.二班同學(xué)的記憶史上一定不會(huì)忘記2017年9月23和24這兩個(gè)幸福的日子,天南地北、四面八方...
    伊人_a05b閱讀 828評(píng)論 0 1
  • 這是詹姆斯連續(xù)進(jìn)東部決賽的第八年。 這八年的夏天里,不管是詹黑還是詹密,都會(huì)搖頭驚嘆,這個(gè)男人的極限到底在哪里。 ...
    強(qiáng)壯左手閱讀 1,682評(píng)論 6 12
  • 窗前最后一點(diǎn)雪花從半空中搖搖晃晃地飄落 放下手中的筆,遠(yuǎn)遠(yuǎn)望去,窗外一片銀裝素裹的世界 地理課...
    夜亮閱讀 304評(píng)論 0 0

友情鏈接更多精彩內(nèi)容