Java動態代理和反射機制

MoreThinking發表於2019-01-23

  反射機制

    Java語言提供的一種基礎功能,通過反射,我們可以操作這個類或物件,比如獲取這個類中的方法、屬性和構造方法等。

  動態代理:分為JDK動態代理、cglib動態代理(spring中的動態代理)。

    靜態代理

      預先(編譯期間)確定了代理者與被代理者之間的關係,也就是說,若代理類在程式執行前就已經存在了,這種情況就叫靜態代理

    動態代理

      代理類在程式執行時建立的代理方式。也就是說,代理類並不是在Java程式碼中定義的,而是在執行期間根據我們在Java程式碼中的“指示”動態生成的。

 

  動態代理比靜態代理的優勢在於:

    動態代理可以很方便的對代理類的函式進行統一的處理(invoke),而不是修改每個代理類的函式,更靈活和擴充套件。

 

  JDK的動態代理(依賴於介面

    在Java的動態代理機制中,有兩個重要的類或介面,一個是InvocationHandler介面,另一個是Proxy類。

    InvocationHandler介面是給動態代理類實現的,負責處理被代理物件的操作

    Proxy類是用來建立動態代理類例項物件的,只有得到這個物件,才能呼叫需要代理的方法。

         動態代理的代理類是在靜態代理類上進行修改,將動態代理類實現InvocationHandler介面,重寫Invoke方法,Invoke方法通過傳入的被代理類方法和引數來執行。

    如下例項:

public interface AppService {
	void createApp(String name);
	void deleteApp(String name);
}

//代理類(比如微商代理)
public class AppServiceImpl implements AppService{

	@Override
	public void createApp(String name) {
		System.out.print("App["+name+"] has been created.");
	}

	@Override
	public void deleteApp(String name) {
		System.out.print("App["+name+"] has been delete.");
	}
}


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

public class LoggerInterceptor implements InvocationHandler {
    private Object target; //委託類(被代理類)的例項,比如廠家
    public LoggerInterceptor(Object target){  
        this.target = target;  
    }  
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
		System.out.println("Entered "+target.getClass().getName()+"-"+method.getName()+",with arguments{"+args[0]+"}");  
        Object result = method.invoke(target, args);
       //呼叫目標物件的方法  (呼叫廠家的方法(createApp)及引數(Kevin Test))
        System.out.println("Before return:"+result);  
        return result;  
	}

}

  

import java.lang.reflect.Proxy;

public class test {

public static void main(String[] args) {
    AppService target = new AppServiceImpl();//生成目標物件 (代理類的物件)
    //接下來建立代理物件 
    AppService proxy = (AppService) Proxy.newProxyInstance( 
    target.getClass().getClassLoader(), 
    target.getClass().getInterfaces(), new LoggerInterceptor(target)); 
    proxy.createApp("Kevin Test1"); 
    proxy.deleteApp("Kevin Test2"); 
  }

}

/**
* 1、jdk的動態代理實現方式是依賴於介面的,首先使用介面來定義好操作規範。
* 2、通過proxy類產生的代理物件呼叫被代理物件的操作。
* 3、而這個操作又被分發給InvocationHandler介面的invoke方法具體執行
* 
* 在java的動態代理機制中,有兩個重要的類或介面,一個是InvocationHandler介面、另一個則是 Proxy類,這個類和介面是實現我們動態代理所必須用到的。
InvocationHandler介面是給動態代理類實現的,負責處理被代理物件的操作的,而Proxy是用來建立動態代理類例項物件的,因為只有得到了這個物件我們才能呼叫那些需要代理的方法。
* 
* 此方法的引數含義如下 
proxy:代表動態代理物件 
method:代表正在執行的方法 
args:代表當前執行方法傳入的實參 
返回值:表示當前執行方法的返回值
* 
* 如上:
* 使用了Proxy類的newProxyInstance方法生成代理物件,然後用這個物件去呼叫createApp()和deleteApp()方法,
* 其實系統會將這2個方法分發給invoke()方法區執行。其中proxy物件的類是系統幫我們動態建立了,其實實現了我們的業務介面AppService
* 
*/

  

  cglib動態代理(繼承方式

  cglib動態代理中使用MethodInterceptor來實現動態代理類。

  攔截器MethodInterceptor中就是由MethodProxy的InvokSuper方法呼叫代理方法的。

  MethodProxy類生成代理方法和代理方法的簽名。

 

  JDK動態代理和Cglib動態代理的區別:

  1、JDK動態代理是實現了被代理物件的介面,Cglib是繼承了被代理物件。

  2、Cglib因為是繼承機制,所以無法代理被final修飾的方法。

  3、JDK和Cglib都是在執行期間生產位元組碼,JDK是直接寫class位元組碼,Cglib使用ASM框架寫class位元組碼;cglib代理實現更復雜,生成代理類比JDK效率低。

  4、JDK呼叫代理方法,是通過反射實現機制呼叫,cglib是通過Fashclass機制直接呼叫方法,效率更高。

 

  Fastcalss機制:

    為代理類和被代理類個生成一個class,這個class會為代理類或被代理類的方法分配一個index。

    這個index當做一個入參,Fashclass就可以直接定位要呼叫的方法,並直接進行呼叫。這樣省去了反射呼叫,所以效率高。

 

相關文章