Java代理(靜態、動態)
阿新 • • 發佈:2021-01-12
一、靜態代理
代理類和被代理類繼承同一個介面,在代理類事例時需要被代理類作為引數,這樣就呼叫到被代理類了;
缺點:每個被代理類都需要寫一個對應的代理類;
public class Target{ void run(){ System.out.println("run Target!"); } } public class Proxy extends Target{ private Target target; Proxy(Target target){ this.target = target; } @Override void run(){ System.out.println("proxy do something else!"); this.target.run(); } }
//使用 void test(){ Target target = new Target(); Proxy proxy = new Proxy(target); //呼叫代理的方法來替代直接呼叫原方法proxy.run(); }
二、動態代理
1、jdk動態代理
代理類需要繼承invocationhandler藉口,原理也是基與介面,類似靜態代理;
缺點:沒有寫介面的被代理類不能被代理,通過反射實現呼叫,比直接呼叫慢;
public interface TargetInter{ void run(); }
public class Target implements TargetInter{ public void run(){ System.out.println("run Target!"); } }public class Myproxy implements InvocationHandler{ private Object obj; public Object bind(Object obj){ this.obj = obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("jdk dynamic proxy"); method.invoke(obj, args); return null; } }
void test(){ Target target = new Target(); TargetInter myproxy = (TargetInter) new Myproxy().bind(target); myproxy.run(); }
---------------------------------------------------- test()結果: jdk dynamic proxy run Target!
2、動態代理CGLIB
原理:執行時動態的生成一個被代理類的子類(通過ASM位元組碼處理框架實現),子類重寫了被代理類中所有非final的方法。在子類中採用方法攔截的技術攔截所有父類方法的呼叫,然後植入AOP邏輯。
缺點:帶final修飾的被代理類不能被代理
public class Target { public void run(){ System.out.println("run Target!"); } } public class Myproxy { Object obj; public Object bind(final Object target){ this.obj = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(obj.getClass()); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable{ System.out.println("CGLIB dynamic proxcy"); Object res = method.invoke(target, args); return res; } } ); return enhancer.create(); } }
void test(){ Target target = new Target(); Target proxy = (Target) new Myproxy().bind(target); }
注意:
代理目標物件不能是內部類(因為內部類的建立依賴外部類),如果是內部類,cglib代理內部會獲取到一個有參建構函式(引數是外部類物件,如果實在需要代理一個內部類,可以通過傳遞構造引數實現)
Cglib代理預設建立一個預設建構函式的目標物件,如果目標物件存在有參建構函式,Cglib進行代理時需要指定建構函式的引數,或者在目標物件上必須存在預設建構函式,否則丟擲異常Superclass has no null constructors but no arguments were given(可以通過傳遞構造引數建立代理類)
3、spring代理機制
優先使用jdk動態代理,如果物件沒介面則使用CGLIB代理
其他:
開閉原則:一個軟體實體如類,模組和函式應該對擴充套件開放,對修改關閉。
一個軟體實體應該通過擴充套件來實現變化,而不是通過修改已有的程式碼來實現變化的。