Java動態代理和cglib動態代理

1、何爲動態代理?html

    建議看動態代理前,先看看反射 點擊這裏java

  先看一個小案例,假設有咖啡類A,B,C。有添加物類a,b,c,。如今對咖啡類進行加強。( 好比像向啡中加糖,牛奶等這個意思)。程序員

  對一個類進行加強能夠有三種方式:spring

  1.繼承,能夠直接繼承父類的屬性和方法,在新增本身的屬性和方法。那麼咱們對每種咖啡進行加強,一共須要寫3*3個繼承類來完成。加強的對象和內容都不可變。數組

  2.裝飾者模式,在須要加強的類中組合一個須要加強的屬性。那麼咱們須要寫三個組合咖啡類,每一個類都添加一個添加物接口屬性,向添加物接口傳不一樣的實現就能夠對該咖啡類進行不一樣的加強。加強的內容可變,但加強的對象是不可變的。ide

  3.動態代理,經過一個代理工廠,向其傳入須要加強的咖啡類和添加物類,其自動生成一個加強後的代理類。咱們只須要實現代理工廠這個類便可。測試

2、java動態代理的實現this

  java中有一個類Proxy,其有一個靜態方法Object proxy = Proxy.newProxyInstance(ClassLoader classLoader , Class[] interfaces , InvocationHandler); 經過此方法就能夠獲得一個代理對象。下面說一下這三個參數。spa

  ClassLoader 類加載器,負責加載代理的類加載器。關於類加載器詳細解答 點擊這裏代理

  Class[] 目標類所實現的接口們,用Class對象數組表示。

  Invocationhandler 處理器,當調用代理類proxy的方法時,其實是調用處理器的 invoke()方法。下面在對invoke方法進行詳解。

  Object invoke(Object proxy , Method method , Object[] args ) ;

  proxy:代理對象。  method: 執行的代理對象的方法。 args: 調用代理對象的方法時傳入的參數。

  下面貼代碼實現

目標類接口

package cn.edu;

public interface Waiter {

    public void server();
}

 

前置通知接口

package cn.edu;

public interface AdviceBefore {

    public void before();
}

後置通知接口

package cn.edu;

public interface AdviceAfter {

    public void after();
}

代理工廠

package cn.edu;

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

/**
 * 代理工廠,用於生產代理對象
 * 
 * @author WangHang
 *
 */
public class ProxyFactory {

    private Object target;               // 目標對象

    private AdviceBefore adviceBefore;   // 前置通知

    private AdviceAfter adviceAfter;     // 後置通知

    // 生成代理對象核心方法
    public Object getProxyInstance() {

        ClassLoader loader = ProxyFactory.class.getClassLoader();    // 類加載器
        Class[] interfaces = target.getClass().getInterfaces();      // 類所實現的接口

        InvocationHandler h = new InvocationHandler() {              // 處理器
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (adviceBefore != null) {
                    adviceBefore.before();                           // 調用前置通知
                }
                Object result = method.invoke(target, args);         // 調用目標方法
                if (adviceAfter != null) {                           // 調用後置通知
                    adviceAfter.after();
                }
                return result;
            }
        };

        Object proxy = Proxy.newProxyInstance(loader, interfaces, h); // 經過給定的三個參數生成動態代理對象
        return proxy;                                                 // 返回代理對象
    }

    public AdviceBefore getAdviceBefore() {
        return adviceBefore;
    }

    public void setAdviceBefore(AdviceBefore adviceBefore) {
        this.adviceBefore = adviceBefore;
    }

    public AdviceAfter getAdviceAfter() {
        return adviceAfter;
    }

    public void setAdviceAfter(AdviceAfter adviceAfter) {
        this.adviceAfter = adviceAfter;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

}

測試,接口的實現用的匿名內部類

package cn.edu;

import org.junit.Test;

public class ProxyTest {

    @Test
    public void fun() {
        Waiter waiter = new Waiter() {                         //生成目標對象
            @Override
            public void server() {
                System.out.println("服務中。。。");
            }
        };
        
        AdviceBefore before = new AdviceBefore() {             //生成前置通知
            @Override
            public void before() {
                System.out.println("歡迎光臨!");
            }
        };
        
        AdviceAfter after = new AdviceAfter() {
            @Override
            public void after() {
                System.out.println("謝謝光臨!");                //生成後置通知
            }
        };
        
        ProxyFactory proxyFactory = new ProxyFactory();        //生成工廠
        
        proxyFactory.setTarget(waiter);                          //設置目標對象
        proxyFactory.setAdviceBefore(before);                    //設置前置通知
        proxyFactory.setAdviceAfter(after);                      //設置後置通知
        
        Waiter proxy = (Waiter)proxyFactory.getProxyInstance();  //得到代理對象
        
        proxy.server();                                          //調用目標對象的方法
    }
    
}

運行結果:

3、cglib動態代理的實現

   java動態代理的實現要求目標類實現接口,若是沒有接口就沒法完成動態代理。cglib(Code Genaration Liarbry)是一個強大的Code生成類庫 ,它能夠在程序運行期間擴展Java類。它不要求目標類實現接口,它採用的是繼承的方式來擴展目標類。

  下面用cglib實現代理,其他類不用變,只需更改代理工廠,而且給Waiter添加一個實現類WaiterImpl,cglib代理是須要類的,用匿名類是會報錯的。更改的部分以下。

Waiter實現類

package cn.edu.cglibProxy;

public class WaiterImpl implements Waiter {

    @Override
    public void server() {
        // TODO Auto-generated method stub
        System.out.println("服務中。。。");
    }

}

代理工廠類

package cn.edu.cglibProxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

/**
 * 代理工廠,用於生產代理對象
 * 
 * @author WangHang
 *
 */
public class ProxyFactory {

    private Object target;             // 目標對象

    private AdviceBefore adviceBefore; // 前置通知

    private AdviceAfter adviceAfter;   // 後置通知

    // 生成代理對象核心方法
    public Object getProxyInstance() {

        Enhancer enhancer = new Enhancer();
        
        enhancer.setSuperclass(target.getClass());
        
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                adviceBefore.before();
                Object result = method.invoke(target, args);
                adviceAfter.after();
                return result;
            }
        });
        Object proxy = enhancer.create();
        return proxy;            // 返回代理對象
    }

    public AdviceBefore getAdviceBefore() {
        return adviceBefore;
    }

    public void setAdviceBefore(AdviceBefore adviceBefore) {
        this.adviceBefore = adviceBefore;
    }

    public AdviceAfter getAdviceAfter() {
        return adviceAfter;
    }

    public void setAdviceAfter(AdviceAfter adviceAfter) {
        this.adviceAfter = adviceAfter;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

}

測試類

package cn.edu.cglibProxy;

import org.junit.Test;

public class ProxyTest {

    @Test
    public void fun() {
        
        Waiter waiter = new WaiterImpl();
        
        AdviceBefore before = new AdviceBefore() {             //生成前置通知
            @Override
            public void before() {
                System.out.println("歡迎光臨!");
            }
        };
        
        AdviceAfter after = new AdviceAfter() {
            @Override
            public void after() {
                System.out.println("謝謝光臨!");                //生成後置通知
            }
        };
        
        ProxyFactory proxyFactory = new ProxyFactory();        //生成工廠
        
        proxyFactory.setTarget(waiter);                          //設置目標對象
        proxyFactory.setAdviceBefore(before);                    //設置前置通知
        proxyFactory.setAdviceAfter(after);                      //設置後置通知
        
        Waiter proxy = (Waiter)proxyFactory.getProxyInstance();  //得到代理對象
        
        proxy.server();                                          //調用目標對象的方法
    }
    
}

測試結果

 

4、總結  

  經過上面的兩個例子,能夠體會到動態代理的優點。靜態代理是由程序員手動編寫好源代碼,在程序運行時已經存在了class文件,而動態代理則是在運行時根據須要動態生成的,不只減小了工做量,使用時也更加靈活。

相關文章
相關標籤/搜索