23中設計模式總結八追加:代理模式之動態代理

靜態代理與動態代理的區別

靜態代理與動態代理的主要區別在於:靜態代理通常只負責代理一個指定的類,而動態代理可以用於代理一系列類,這一系列類通常都實現了同一個接口
可以將靜態代理看成是java中定義的常量,動態代理可以看成是java中定義的變量,在編譯期可以確定常量的值,而變量的值需要在運行期纔可以確定。對應的代理也是一樣,靜態代理事先知道要代理的是什麼,而動態代理不知道要代理的是什麼東西,只有在運行時才知道


動態代理的兩種實現

java中的動態代理主要可以通過兩種方式來實現:JDK代理和CGLIB代理,下面將分別通過一個例子來詳細介紹這兩個代理


JDK代理的實現

JDK代理是jdk提供的一種動態代理實現,下面將通過一個實例來簡單介紹一下
一個Hello實體
package org.smart4j.chapter2.acproxy;

/**
 * 靜態代理類
 * @author chengxi
 */
public interface Hello {

    /**
     * 簡單的問候
     * @param str
     */
    public void say(String str);
}
Hello接口的一個實現類
package org.smart4j.chapter2.acproxy;

/**
 * 被代理類
 * @author chengxi
 */
public class HelloImpl implements Hello {

    @Override
    public void say(String str){
        System.out.println("hello  " + str);
    }
}
JDK代理方式實現的一個代理類
package org.smart4j.chapter2.ajproxy;

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

/**
 * JDK動態代理類
 * 使用JDK代理有一個條件:被代理的類必須有父接口,否則無法Proxy.newProxyInstance()動態創建代理
 * @author chengxi
 */
public class DynamicProxy implements InvocationHandler{

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        before();
        Object result = method.invoke(target,args);
        after();
        return result;
    }

    public void before(){
        System.out.println("before say hello");
    }

    public void after(){
        System.out.println("after say hello");
    }
}
JDK代理測試類
package org.smart4j.chapter2.ajproxy;

import java.lang.reflect.Proxy;

/**
 * 靜態代理測試main類
 * @author chengxi
 */
public class Main {

    public static void main(String[] args){

        Hello hello = (Hello) new HelloImpl();

        DynamicProxy dynamicProxy = new DynamicProxy(hello);

        /**
         * 調用jdk提供的該方法動態創建一個Hello接口代理類
         */
        Hello helloProxy = (Hello) Proxy.newProxyInstance(
                hello.getClass().getClassLoader(),
                hello.getClass().getInterfaces(),
                dynamicProxy
        );

        helloProxy.say("chengxi");
    }
}
從上面的代碼中可以看到,想要動態創建一個對應的代理類需要使用JDK提供的Proxy類的newProxyInstance方法,該方法需要提供三個參數:ClassLoader、Interfaces和Proxy
使用JDK代理的一個缺點在於:它代理的對象是接口,所以需要代理的類必須有實現父接口,否則將無法動態創建對應的代理類。那麼有沒有一種代理方式不需要我們來爲每一個代理類提供一個父接口呢?有的別急,接下來就要介紹這種方式了


CGLIB代理

同樣使用上面的Hello和HelloImpl,接下來創建CGLIB實現的代理類
package org.smart4j.chapter2.acproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLIB動態代理類
 * 使用該代理方式可以無需被代理類有父接口,面向的是方法代理
 * @author chengxi
 */
public class DynamicProxy implements MethodInterceptor {

    /**
     * 實現單例模式
     */
    private volatile static DynamicProxy dynamicProxy;

    private DynamicProxy(){
    }

    public static DynamicProxy getInstance(){
        if(dynamicProxy == null){
            synchronized (DynamicProxy.class){
                if(dynamicProxy == null){
                    dynamicProxy = new DynamicProxy();
                }
            }
        }
        return dynamicProxy;
    }

    /**
     * 動態獲取對應的代理類
     * @param cls
     * @param <T>
     * @return
     */
    public <T> T getProxy(Class<T> cls){

        return (T) Enhancer.create(cls,this);
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                          MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(obj, args);
        after();
        return result;
    }

    public void before(){
        System.out.println("before say hello");
    }

    public void after(){
        System.out.println("after say hello");
    }

}
CGLIB代理測試類
package org.smart4j.chapter2.acproxy;

/**
 * CGLIB動態代理測試類
 * @author chengxi
 */
public class Main {

    public static void main(String[] args){

        Hello helloProxy = DynamicProxy.getInstance().getProxy(HelloImpl.class);
        helloProxy.say("chengxi");
    }
}
CGLIB代理相對於JDK代理的好處在於:它的代理對象是面向方法的,所以無需代理類實現任何的接口
這裏需要注意的是:CGLIB代理和JDK代理不一樣,在java中沒有提供它的jar包,所以需要我們自己導入,而這裏我使用的是maven,所以我直接使用pom.xml來導入它的依賴
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章