1. 程式人生 > 程式設計 >Java代理設計模式(Proxy)的幾種具體實現

Java代理設計模式(Proxy)的幾種具體實現

Proxy是一種結構設計模型,主要解決物件直接訪問帶來的問題,代理又分為靜態代理和動態代理(JDK代理、CGLIB代理。靜態代理:又程式建立的代理類,或者特定的工具類,在平時開發中經常用到這種代理模式,這種一般在程式執行前就已經生成對應的class檔案;動態代理:在程式執行時通過反射機制動態建立。

下面通過一個場景實現以下三種代理方式

  • 步驟一:定義商店介面(Subject)
  • 步驟二:個人店家運營(RealSubject)
  • 步驟三:平臺運營(proxy)
  • 步驟四:個體消費(client)
    file

幾種代理方式都會用到Subject、RealSubject,現在這裡定義Store.java(Subject):定義兩個介面operate(運營),business(交易)。

public interface Store {
    /**
     * 店鋪運營
     */
    public void operate();

    /**
     * 店鋪交易
     */
    public void business();
}
複製程式碼

PersonStore.java(RealSubject) 單個使用者運營

public class PersonStore implements Store {
    @Override
    public void operate() {
        System.out.println("個人商店運營");
    }

    @Override
    public void business() {
        System.out.println("個人商店交易");
    }
}複製程式碼

靜態代理

靜態代理的實現比較簡單,代理類通過實現與目標物件相同的介面,並在類中維護一個代理物件,這種場景用於個體商家比較少的情況,如果多的話代理類十分繁多、不易維護

建立靜態代理類

ProxyStroe.java(proxy):在代理平臺運營是收取管理費用100,這個代理類需要實現Store介面,並制定目標類target(PersonStore)。

public class ProxyStroe implements Store{

    private Store personStore = new PersonStore();

    @Override
    public void operate() {
        System.out.println("收取管理費用100元");
        personStore.operate();
    }

    @Override
    public void business() {
        personStore.business();
    }
}複製程式碼

靜態代理呼叫

StaticConsumer.java(client):通過建立ProxyStroe去代理PersonStore,並進行操作。

public class StaticConsumer {

    public static void main(String[] args) {
        ProxyStroe store = new ProxyStroe();
        store.operate();
        store.business();
    }
}複製程式碼

動態代理

JDK代理

動態代理類是通過介面實現的,利用攔截器(攔截器必須實現InvocationHanlder)加上反射機制生成一個實現代理介面的匿名類,在呼叫具體方法前呼叫InvokeHandler來處理。

建立jdk代理類

JDKStoreHandler.java(proxy):通過實現InvocationHandler介面的invoke方法,在裡面進行反射呼叫,newProxyInstanse通過目標物件建立真是物件。

public class JDKStoreHandler implements InvocationHandler {

    /**
     * 目標物件
     */
    private Object targetObject;

    @Override
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
        if("operate".equals(method.getName())){
            System.out.println("收取管理費用100元");
        }
        return method.invoke(targetObject,args);
    }

    /**
     *
     * @param targetObject
     * @return
     */
    public Object newProxyInstanse(Object targetObject){
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
    }
}複製程式碼

JDK代理物件呼叫

JDKDynamicConsumer.java(proxy):通過JDKStoreHandler的newProxyInstanse建立真實目標物件,並呼叫介面的方法

public class JDKDynamicConsumer {

    public static void main(String[] args) {
        Store store = (Store)new JDKStoreHandler().newProxyInstanse(new PersonStore());
        store.operate();
        store.business();
    }
}
複製程式碼

CGLIB代理

cglib動態代理是利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。

建立cglib代理物件

CglibProxy.java(proxy):需要實現MethodInterceptor的intercept方法,並進行反射呼叫。通過createProxyObject建立真實物件,這裡是根據目標物件直接生成物件。

public class CglibProxy implements MethodInterceptor{

    /**
     * CGlib需要代理的目標物件
     */
    private Object targetObject;

    public Object createProxyObject(Object targetObject){
        this.targetObject = targetObject;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetObject.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;
    }

    @Override
    public Object intercept(Object o,Object[] objects,MethodProxy methodProxy) throws Throwable {
        if("operate".equals(method.getName())){
            System.out.println("收取管理費用100元");
        }
        return method.invoke(targetObject,objects);
    }
}複製程式碼

CGLib代理物件呼叫

CglibDynamicConsumer.java(client):通過CglibProxy的createProxyObject建立真實目標物件,對進行方法呼叫

public class CglibDynamicConsumer {

    public static void main(String[] args) {
        Store store = (Store)new CglibProxy().createProxyObject(new PersonStore());
        store.operate();
        store.business();
    }
}
複製程式碼

原始碼已上傳:https://github.com/itrickzhang/proxy-demo