jdk動態代理 和 CGLIB動態代理 詳解
阿新 • • 發佈:2019-01-24
1. 靜態代理模式
//目標介面
public interface IDoSomething {
public void doSomething();
}
//目標物件
public class DoSomething implements IDoSomething{
public void doSomething() {
System.out.println(" do something ");
}
}
如果使用繼承的方式,可以直接繼承後,覆蓋父類方法,然後在此方法內部與呼叫父類的方法,實現代理此種形式無需父類實現介面public class SubDoSomething extends DoSomething{ @Override public void doSomething() { System.out.println(" before "); super.doSomething(); System.out.println(" end "); } public static void main(String[] args) { DoSomething t = new SubDoSomething(); t.doSomething(); } }
如果使用介面的形式,那麼代理類必須跟被代理類實現同一個介面,代理類擁有被代理的物件在代理類實現介面方法時,直接呼叫被代理的物件來實現
public class StaticProxyDoSomething implements IDoSomething{ IDoSomething t; public StaticProxyDoSomething(IDoSomething t) { this.t = t; } @Override public void doSomething() { System.out.println(" before "); t.doSomething(); System.out.println(" end "); } public static void main(String[] args) { IDoSomething t1 = new DoSomething(); IDoSomething t2 = new StaticProxyDoSomething(t1); t2.doSomething(); } }
靜態代理模式,必須針對單個指定的類進行代理,如果有多個類需要代理的話,需要寫太多重複的代理類
而且如果被代理類增加一個方法,對應的代理類都必須進行修改
2. JDK動態代理,可以直接使用反射的方式,動態呼叫被代理類的方法,具體步驟:
1.寫一個通用代理類,實現java.lang.reflect.InvocationHandler介面,實現invoke()方法
2.代理類需要通過反射,來呼叫被代理的物件,那麼首先需要獲取被代理的物件,一般可以通過建構函式傳入
然後需要構造一個被代理的物件(Proxy.newProxyInstance()),最後在invoke方法裡面呼叫相應方法
實際上,JDK直接生成了一個代理類的class檔案類似:$DoSomethingProxy.class,這個class實現了IDoSomething介面並繼承了Proxy
(因為要繼承Proxy,所以不能直接繼承DoSomething,那麼就必須要實現一個介面了,這就是JDK動態代理必須有介面的原因)
package com.ourselvesoft.catering.test.other.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandler implements InvocationHandler{
//獲取目標物件
public Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
//使用反射呼叫對應的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(" before ");
Object result = method.invoke(target, args);
System.out.println(" end ");
return result;
}
//獲取代理物件
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public static void main(String[] args) {
IDoSomething t = new DoSomething();
MyInvocationHandler handler = new MyInvocationHandler(t);
//IDoSomething proxy = (IDoSomething)Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), handler);
IDoSomething proxy = (IDoSomething)handler.getProxy();
proxy.doSomething();
}
}
3. cglib動態代理
cglib動態代理則無需被代理類實現任何介面(但是cglib採用了動態建立子類的方法,因而無法代理final方法)
首先實現net.sf.cglib.proxy.MethodInterceptor 介面,實現其intercept方法(getProxy傳入被代理class)
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
// 設定需要建立子類的類
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
// 通過位元組碼技術動態建立子類例項
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(" before ");
// 通過代理類呼叫父類中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println(" after ");
return result;
}
public static void main(String[] args) {
CGLibProxy proxy = new CGLibProxy();
DoSomething t = (DoSomething)proxy.getProxy(DoSomething.class);
t.doSomething();
}
}
spring aop 預設使用JDK 動態代理,在沒有介面的情況下,會使用cglib動態代理