Java靜態代理和動態代理

Java靜態代理和動態代理

一、概述

代理是一種模式,提供了對目標對象的間接訪問方式,即通過代理訪問目標對象。如此便於在目標實現的基礎上增加額外的功能操作,前攔截,後攔截等,以滿足自身的業務需求,同時代理模式便於擴展目標對象功能的特點也爲多人所用。

二、圖形描述

在這裏插入圖片描述

三、靜態代理

靜態代理的實現比較簡單,代理類通過實現與目標對象相同的接口,並在類中維護一個代理對象。通過構造器塞入目標對象,賦值給代理對象,進而執行代理對象實現的接口方法,並實現前攔截,後攔截等所需的業務功能。

/**
 * 目標對象實現的接口
 * @author jiyukai
 */
public interface BussinessInterface {
 
    void execute();
}
 
/**
 * 目標對象實現類
 * @author jiyukai
 */
public class Bussiness implements BussinessInterface{
 
    @Override
    public void execute() {
        System.out.println("執行業務邏輯...");
    }
}
 
/**
 * 代理類,通過實現與目標對象相同的接口
 * 並維護一個代理對象,通過構造器傳入實際目標對象並賦值
 * 執行代理對象實現的接口方法,實現對目標對象實現的干預
 * @author jiyukai
 */
public class BussinessProxy implements BussinessInterface{
     
    private BussinessInterface bussinessImpl;
     
    public BussinessProxy(BussinessInterface bussinessImpl) {
        this.bussinessImpl = bussinessImpl;
    }
     
    @Override
    public void execute() {
        System.out.println("前攔截...");
        bussinessImpl.execute();
        System.out.println("後攔截...");
    }
}

靜態代理的總結

優點:可以做到不對目標對象進行修改的前提下,對目標對象進行功能的擴展和攔截。

缺點:因爲代理對象,需要實現與目標對象一樣的接口,會導致代理類十分繁多,不易維護,同時一旦接口增加方法,則目標對象和代理類都需要維護。

四、動態代理
  
  動態代理是指動態的在內存中構建代理對象(需要我們制定要代理的目標對象實現的接口類型)。

  1. 動態代理的分類:基於接口的動態代理(JDK官方)和基於子類的動態代理(cglib)
  2. 具體演示
    (1)基於接口的動態代理
package com.renjing.proxy;

/**
 * 此爲被代理類的接口
 * 一個經紀公司的要求:
 * 能做基本的表演和危險的表演
 */

public interface IActor {
    /**
     * 基本的演出
     *
     */
    public void basicAct(float money);

    /**
     * 危險演出
     */
    public void dangerAct(float money);
}
package com.renjing.proxy;
/**
 * 此爲實際的被代理類
 */
public class Actor implements IActor {

    /**
     * 基本演出
     * @param money
     */
    @Override
    public void basicAct(float money) {
        System.out.println("拿到錢,開始基本的表演:"+money);
    }

    /**
     * 危險演出
     * @param money
     */
    @Override
    public void dangerAct(float money) {
        System.out.println("拿到錢,開始危險的表演:"+money);
    }
}
package com.renjing.proxy;

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

public class Client {
    public static void main(String[] args) {
        //一個劇組找演員
        Actor actor = new Actor();//直接

        /**
         * 代理:間接。
         * 獲取代理對象:
         *  要求:被代理類最少實現一個接口。但是代理類無需實現任何接口。
         * 創建的方式:Proxy.newProxyInstance(三個參數)
         * 參數含義:
         * ClassLoader:和被代理對象使用相同的類加載器。
         * Interfaces:和被代理對象具有相同的行爲。實現相同的接口。
         * InvocationHandler:如何代理。
         *      策略模式:使用場景是:
         *          數據有了,目的明確。
         *          達成目標的過程,就是策略。
         */
        IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * 執行被代理對象的任何方法,都會經過該方法。
                 * 此方法有攔截的功能。
                 *  參數:
                 *      proxy:代理對象的引用。不一定每次都用得到。
                 *      method:當前執行的方法對象。
                 *      args:執行方法所需的參數。
                 *  返回值:
                 *      當前執行方法的返回值。
                 */
                String name = method.getName();
                Float money = (Float)args[0];
                Object rtValue = null;
                //每個經紀公司對不同演出收費不一樣,此處開始判斷
                if ("basicAct".equals(name)) {
                    //基本演出
                    if (money > 2000) {
                        //看上去劇組是給了 8000,實際到演員手裏只有 4000
                        //這就是我們沒有修改原來 basicAct 方法源碼,對方法進行了增強
                        rtValue = method.invoke(actor, money/2);
                    }
                }
                if ("dangerAct".equals(name)) {
                    //基本演出
                    if (money > 5000) {
                        //看上去898劇組是給了 8000,實際到演員手裏只有 4000
                        //這就是我們沒有修改原來 basicAct 方法源碼,對方法進行了增強
                        rtValue = method.invoke(actor, money/2);
                    }
                }
                return rtValue;
            }
        });
        //沒有經濟公司的時候,直接找演員。
        actor.basicAct(100f);
        actor.dangerAct(500f);

        //劇組無法直接聯繫演員,而是由經紀公司找的演員
        proxyActor.basicAct(8000f);
        proxyActor.dangerAct(50000f);
    }
}

(2)基於子類的動態代理

package com.renjing.cglib;
/**
 * 此爲被代理類:不能爲最終類
 */
public class Actor {
    /**
     * 基本演出
     * @param money
     */
    public void basicAct(float money) {
        System.out.println("CGLIB拿到錢,開始基本的表演:"+money);
    }

    /**
     * 危險演出
     * @param money
     */
    public void dangerAct(float money) {
        System.out.println("CGLIB拿到錢,開始危險的表演:"+money);
    }
}
package com.renjing.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class Client {
    public static void main(String[] args) {
        //一個劇組找演員
        final Actor actor = new Actor();//直接(注意在匿名內部類中只能引用final類型的外部變量)

        /**
         * 基於子類的動態代理
         *  要求:被代理的對象不能是最終類。
         *  用到的類:Enhancer
         *  用到的方法:create(Class,Callback)
         *  方法的參數:
         *      Class:被代理對象的字節碼。
         *      Callback:如何代理。
         */
        Actor cglibActor = (Actor) Enhancer.create(actor.getClass(), new MethodInterceptor() {
            /**
             * 執行被代理對象的任何方法,都會經過該方法。在此方法內部就可以對被代理對象的任何方法進行增強。
             *
             * 參數:
             *      前三個和基於接口的動態代理是一樣的。
             *      MethodProxy:當前執行方法的代理對象。
             *      返回值:當前執行方法的返回值。
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                String name = method.getName();
                Float money = (Float) args[0];
                Object rtValue = null;
                //每個經紀公司對不同演出收費不一樣,此處開始判斷
                if ("basicAct".equals(name)) {
                    //基本演出
                    if (money > 2000) {
                        //看上去劇組是給了 8000,實際到演員手裏只有 4000
                        //這就是我們沒有修改原來 basicAct 方法源碼,對方法進行了增強
                        rtValue = method.invoke(actor, money / 2);
                    }
                }
                if ("dangerAct".equals(name)) {
                    //基本演出
                    if (money > 5000) {
                        //看上去898劇組是給了 8000,實際到演員手裏只有 4000
                        //這就是我們沒有修改原來 basicAct 方法源碼,對方法進行了增強
                        rtValue = method.invoke(actor, money / 2);
                    }
                }
                return rtValue;
            }
        });
        cglibActor.basicAct(10000);
        cglibActor.dangerAct(100000);
    }
}

動態代理的總結

優點:代理對象無需實現接口,免去了編寫很多代理類的煩惱,同時接口增加方法也無需再維護目標對象和代理對象,只需在事件處理器中添加對方法的判斷即可。

缺點:代理對象不需要實現接口,但是目標對象一定要實現接口,否則無法使用JDK動態代理。

原文地址:https://www.cnblogs.com/jiyukai/p/6958744.html

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