Java代理(靜態/動態 JDK,cglib)

Java的代理模式是應用非常廣泛的設計模式之一,也叫作委託模式,其目的就是爲其他的對象提供一個代理以控制對某個對象的訪問和使用,代理類負責爲委託類預處理消息,過濾消息並轉發消息,以及對消息執行後續處理。

代理就是充當一箇中間人的角色。

 

按照代理的創建時期,代理類可以分爲兩種:

靜態代理:指由程序員直接創建生成源代碼,在對其編譯生成.class文件,在程序運行之前就已經存在

動態代理:在程序運行時,使用java的反射機制動態創建而成。其中動態代理又分爲JDK代理(需要接口)和cglib代理(不需要接口)

 

下面將以程序案例的方式演示Java靜態代理和動態代理的區別

假設現在需要實現計算機的加減乘除操作,現有如下接口和實現類;

複製代碼

 1 package Reflection.proxy; 2  3 /** 4  * Created by : Infaraway 5  * DATE : 2017/3/3 6  * Time : 15:12 7  * Funtion : 計算器的功能 8  */ 9 public interface Calculator {10 11     int add(int a, int b);12     int sub(int a, int b);13 14     void mul(int a, int b);15     void div(int a, int b);16 }

複製代碼

複製代碼

 1 package Reflection.proxy; 2  3 /** 4  * Created by : Infaraway 5  * DATE : 2017/3/3 6  * Time : 15:14 7  * Funtion : 8  */ 9 public class CalculatorImpl implements Calculator{10     @Override11     public int add(int a, int b) {12         //System.out.println("The method add begin...);13         int result = a + b;14         //System.out.println("The method add end...);15         System.out.println(result);16         return result;17     }18 19     @Override20     public int sub(int a, int b) {21         int result = a - b;22         System.out.println(result);23         return result;24     }25 26     @Override27     public void mul(int a, int b) {28         int result = a * b;29         System.out.println(result);30     }31 32     @Override33     public void div(int a, int b) {34         int result = a / b;35         System.out.println(result);36     }37 }

複製代碼

 

如上部分add方法所示,現在希望在每個方法的實現之前打印方法開始和方法結束的日誌信息,那麼最容易的方法就是在源代碼中的每個方法全部加上,但是這樣非常的繁瑣(需要編寫大量的相同的代碼),並且代碼的維護性非常的差!

爲了解決這個問題,我們需要使用代理的方法來解決。

 

靜態代理:

首先我們使用靜態代理的解決方法:

複製代碼

package Reflection.proxy;/**
 * Created by : Infaraway
 * DATE : 2017/3/3
 * Time : 19:51
 * Funtion : java靜態代理類實現 */public class StaticProxy implements Calculator {    private CalculatorImpl calculatorImpl;    public StaticProxy(CalculatorImpl calculatorImpl){        this.calculatorImpl = calculatorImpl;
    }

    @Override    public int add(int a, int b) {
        System.out.println("the add method begin...");        //調用被代理類的方法
        int result = calculatorImpl.add(a, b);
        System.out.println("the add method end...");        return result;
    }

    @Override    public int sub(int a, int b) {
        System.out.println("the sub method begin...");        //調用被代理類的方法
        int result = calculatorImpl.sub(a, b);
        System.out.println("the sub method end...");        return result;
    }

    @Override    public void mul(int a, int b) {
        System.out.println("the mul method begin...");        //調用被代理類的方法
        calculatorImpl.mul(a, b);
        System.out.println("the mul method end...");

    }

    @Override    public void div(int a, int b) {
        System.out.println("the div method begin...");        //調用被代理類的方法
        calculatorImpl.div(a, b);
        System.out.println("the div method end...");
    }
}

複製代碼

 

 

顯然,靜態代理方法並不能改變原來繁瑣的步驟,並且每個代理類只能爲一個藉口服務,這樣的話,程序中必然會存在非常多的代理,並且這樣的代理仍然會產生修改代碼困難的問題;

因此,解決這個問題的一個非常好的辦法出現了,那就是動態代理~

 

動態代理:

動態代理又分爲兩種:JDK代理 和 cglib代理

JDK代理:主要針對的是有接口的情況;

其中JDK動態代理包含了一個接口和一個類:

Proxy類: 
Proxy類是專門完成代理的操作類,可以通過此類爲一個或多個接口動態地生成實現類,此類提供瞭如下的操作方法: 
public static Object newProxyInstance(

      ClassLoader loader,  

      Class<?>[] interfaces, 

      InvocationHandler h)  throws IllegalArgumentException 

/**
 * ClassLoader :由動態代理產生的對象由哪個類加載器來加載 通常情況下和被代理對象使用同樣的類加載器;
 * Class<?>[] : 由動態代理產生的對象必須要實現的接口的Class數組;
 * InvocationHandler : 當具體調用代理對象方法時,將產生的具體行爲; 通常使用匿名內部類的方式實現。
 */

InvocationHandler接口: 
public interface InvocationHandler { 
  public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

參數說明: 

/**
 * @param proxy 被代理的對象
 * @param method: 正在被調用的方法
 * @param args :調用方法時傳入的參數
 * @return 被調用方法的返回值 
 * @throws Throwable
 */



因此,當時用JDK代理方式實現上述的需求時,則如下代碼所示:

複製代碼

 Reflection.proxy; org.junit.Test; java.lang.reflect.InvocationHandler; java.lang.reflect.Method; java.lang.reflect.Proxy;  DynamicJDKProxy {

    @Test      dynamicJDKProxy(){        
        Calculator calculator =  CalculatorImpl();

        Calculator calculatorProxy = (Calculator) Proxy.newProxyInstance(
                calculator.getClass().getClassLoader(),                 Class[]{Calculator.},                 InvocationHandler() {                    
                    @Override                     Object invoke(Object proxy, Method method, Object[] args)  Throwable {                        

                        System.out.println("The method "+method.getName()+" begin...");
                        Object obj = method.invoke(calculator, args);
                        System.out.println("The method "+method.getName()+" end...");                         obj;
                    }
                });
     //測試打印輸出結果
        calculatorProxy.mul(2, 3);         result = calculatorProxy.add(1, 5);
        System.out.println(result);
    }
}

複製代碼

運行結果:

複製代碼

The method mul begins...
6
The method mul ends...
The method add begins...
6
The method add ends...
6

複製代碼

 

雖然上述方法很好的解決了問題,但是JDK動態代理必須依靠接口實現,如果某些類並沒有實現接口,那麼就不能使用JDK代理方式;

所以這裏又給出一種新的代理方法:cglib動態代理來解決接口的問題。

 

cglib代理:針對沒有接口的情況;主要是針對類來實現,主要思想是對指定的目標類來生成一個子類,然後覆蓋子類的方法實現增強。

需要實現MethodInterceptor接口,實現intercept方法。該代理中在add方法前後加入了自定義的切面邏輯,目標類add方法執行語句爲proxy.invokeSuper(object, args);

 

複製代碼

 Reflection.proxy; net.sf.cglib.proxy.Enhancer; net.sf.cglib.proxy.MethodInterceptor; net.sf.cglib.proxy.MethodProxy; java.lang.reflect.Method;  DynamicCglibProxy  MethodInterceptor {    
     Object target;    
     Object createProxy(Object target){        .target = target;
        Enhancer enhancer =  Enhancer();
        enhancer.setSuperclass(.target.getClass());        
        enhancer.setCallback();        
         enhancer.create();
    }    
    @Override     Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy)  Throwable {

        System.out.println("The method "+method.getName()+" begins...");
        Object object = methodProxy.invokeSuper(obj, objects);
        System.out.println("The method "+method.getName()+" ends...");         object;
    }
}

複製代碼

複製代碼

 Reflection.proxy; org.junit.Test;  TestDynamicCglibProxy {
    @Test      testProxy(){
        DynamicCglibProxy CglibProxy =  DynamicCglibProxy();
        CalculatorImpl testCalculator = (CalculatorImpl) CglibProxy.createProxy( CalculatorImpl());
        testCalculator.add(3,5);
    }
}

複製代碼

 

想要更進一步瞭解Java代理模式,則需要認真學習Java的反射機制


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章