1. 程式人生 > 實用技巧 >Java代理(靜態、動態)

Java代理(靜態、動態)

一、靜態代理

代理類和被代理類繼承同一個介面,在代理類事例時需要被代理類作為引數,這樣就呼叫到被代理類了;
缺點:每個被代理類都需要寫一個對應的代理類;

    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代理

其他:
開閉原則:一個軟體實體如類,模組和函式應該對擴充套件開放,對修改關閉。
一個軟體實體應該通過擴充套件來實現變化,而不是通過修改已有的程式碼來實現變化的。