1. 程式人生 > >Java筆記(二十一) 動態代理

Java筆記(二十一) 動態代理

動態代理

一、靜態代理

代理的背後一般至少有一個實際物件,代理的外部功能和實際物件一般是一樣的,

使用者與代理打交道,不直接接觸實際物件。代理存在的價值:

1)節省成本比較高的實際物件建立開銷,按需延遲載入,建立代理時

並不正真建立實際物件,而只是儲存實際物件的地址,在需要時再載入或者建立。

2)執行許可權檢查,代理檢查許可權後再呼叫實際物件。

3)遮蔽網路的差異性和複雜性,代理在本地,而實際物件在其他伺服器上,呼叫

本地代理時,本地代理請求其他伺服器。

靜態代理示例:

public class SimpleStaticProxy {
    //服務介面
    static
interface IService { void sayHello(); } //服務類 static class RealService implements IService { @Override public void sayHello() { System.out.println("hi"); } } //代理類 static class TraceProxy implements IService { private IService realService;
public TraceProxy(IService realService) { this.realService = realService; } @Override public void sayHello() { System.out.println("Start say hi"); realService.sayHello(); System.out.println("End say hi"); } } public
static void main(String[] args) { RealService service = new RealService(); TraceProxy traceProxy = new TraceProxy(service); traceProxy.sayHello(); } }

靜態代理缺陷:如果每個類都需要代理,我們需要為每個類都建立代理類,

實現所有介面,這個工作量相當大。

二、Java SDK代理

所謂動態代理,代理類是動態生成的。

1.用法

public class SimpleJDKDynamicProxy {
    interface IService {
        void sayHello();
        void sayGoodBye();
    }
    static class RealService implements IService {
        @Override
        public void sayHello() {
            System.out.println("hi");
        }
        @Override
        public void sayGoodBye() {
            System.out.println("GoodBye world!");
        }
    }
    static class SimpleInvocateHandler implements InvocationHandler {
        private Object realObj;
        public SimpleInvocateHandler(Object realObj) {
            this.realObj = realObj;
        }
        /**
        * @param proxy 表示代理物件本身,注意它不是被代理的物件,這個引數用處不大
        * @param method 表示正在被呼叫的方法
        * @param args 表示方法的引數
        * */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Start " + method.getName());
            Object result = method.invoke(realObj, args);
            System.out.println("End " + method.getName());
            return result;
        }
    }
    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(),
                new Class<?>[]{IService.class}, new SimpleInvocateHandler(realService));
        proxyService.sayHello();
        proxyService.sayGoodBye();
    }
}
/**
    * @param loader 類載入器
    * @param interfaces 代理類要實現的介面列表
    * @param h 該介面定義了invoke方法,對代理介面所有的方法呼叫都會轉給該方法
    * @return 可以轉換為interfaces陣列中的某個介面型別,注意只能轉換為介面不能轉換為具體的類
    * */
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces, InvocationHandler h);

2.基本原理

建立proxyService的程式碼可以用如下的程式碼代替:

//建立代理類定義,類定義會被快取
Class<?> proxyCls = Proxy.getProxyClass(IService.class.getClassLoader(), new Class<?>[] { IService.class });
//獲取代理類的構造方法
Constructor<?> ctor = proxyCls.getConstructor(new Class<?>[] { InvocationHandler.class });
InvocationHandler handler = new SimpleInvocationHandler(realService);
//建立代理類物件
IService proxyService = (IService) ctor.newInstance(handler);
    final class $Proxy0 extends Proxy implements SimpleJDKDynamicProxyDemo.IService {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
        public $Proxy0(InvocationHandler paramInvocationHandler) {
            super(paramInvocationHandler);
        }
        public final boolean equals(Object paramObject) {
            return((Boolean) this.h.invoke(this, m1,
                    new Object[] { paramObject })).booleanValue();
        }
        public final void sayHello() {
            this.h.invoke(this, m3, null);
        }
        public final String toString() {
            return (String) this.h.invoke(this, m2, null);
        }
        public final int hashCode() {
            return ((Integer) this.h.invoke(this, m0, null)).intValue();
        }
        static {
            m1 = Class.forName("java.lang.Object").getMethod("equals",
                    new Class[] { Class.forName("java.lang.Object") });
            m3 = Class.forName(
                    "laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService")
                    .getMethod("sayHello",new Class[0]);
            m2 = Class.forName("java.lang.Object")
                    .getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object")
                    .getMethod("hashCode", new Class[0]);
        }
    }

三、cglib動態代理

Java SDK動態代理的侷限,它只能為介面建立代理,返回的代理物件也只能轉換到某個介面型別。

public class SimpleCGLibDemo {
    static class RealService {
        public void sayHello() {
            System.out.println("hello");
        }
    }
    static class SimpleInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object object, Method method, Object[] args,
                                MethodProxy methodProxy) throws Throwable {
            System.out.println("start " + method.getName());
            Object result = methodProxy.invokeSuper(object, args);
            System.out.println("end " + method.getName());
            return result;
        }
    }
    private static <T> T getProxy(Class<T> cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setCallback(new SimpleInterceptor());
        return (T) enhancer.create();
    }
    public static void main(String[] args) {
        RealService proxy = getProxy(RealService.class);
        proxy.sayHello();
    }
}

cglib是通過繼承實現的,具體原理略。