1. 程式人生 > >javaAOP動態代理的實現

javaAOP動態代理的實現

AOP的攔截功能是由java中的動態代理來實現的。說白了,就是在目標類的基礎上增加切面邏輯,生成增強的目標類(該切面邏輯或者在目標類函式執行之前,或者目標類函式執行之後,或者在目標類函式丟擲異常時候執行。不同的切入時機對應不同的Interceptor的種類,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。
那麼動態代理是如何實現將切面邏輯(advise)織入到目標類方法中去的呢?下面我們就來詳細介紹並實現AOP中用到的兩種動態代理。
AOP的原始碼中用到了兩種動態代理來實現攔截切入功能:jdk動態代理和cglib動態代理。兩種方法同時存在,各有優劣。jdk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行快取,這樣解決asm生成類過程低效問題)。還有一點必須注意:jdk動態代理的應用前提,必須是目標類基於統一的介面。如果沒有上述前提,jdk動態代理不能應用。由此可以看出,jdk動態代理有一定的侷限性,cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。。

JDK動態代理
在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:  指代我們所代理的那個真實物件
method:  指代的是我們所要呼叫真實物件的某個方法的Method物件
args:  指代的是呼叫真實物件某個方法時接受的引數
public interface Subject {

public void rent();

public void hello(String str);

}
public class RealSubject implements Subject {
@Override
public void rent() {
System.out.println(“I want to rent my house”);
}

@Override
public void hello(String str) {
	System.out.println("hello: " + str);
}

}
每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的例項都關聯到了一個handler,當我們通過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行呼叫。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxy implements InvocationHandler {
//  這個就是我們要代理的真實物件
private Object subject;

// 構造方法,給我們要代理的真實物件賦初值
public DynamicProxy(Object subject) {
	this.subject = subject;
}

public Object invoke(Object object, Method method, Object[] args) throws Throwable {
	//   在代理真實物件前我們可以新增一些自己的操作
	System.out.println("before rent house");

	System.out.println("Method:" + method);

	// 當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫
	method.invoke(subject, args);

	//   在代理真實物件後我們也可以新增一些自己的操作
	System.out.println("after rent house");

	return null;
}

}
測試類
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {
public static void main(String[] args) {
// 我們要代理的真實物件
Subject realSubject = new RealSubject();

	// 我們要代理哪個真實物件,就將該物件傳進去,最後是通過該真實物件來呼叫其方法的
	InvocationHandler handler = new DynamicProxy(realSubject);

	/*
	 * 通過Proxy的newProxyInstance方法來建立我們的代理物件,
	 * 我們來看看其三個引數 第一個引數 handler.getClass().getClassLoader()
	 * 我們這裡使用handler這個類的ClassLoader物件來載入我們的代理物件
	 * 第二個引數realSubject.getClass().getInterfaces(),我們這裡為代理物件提供的介面是真實物件所實行的介面,
	 * 表示我要代理的是該真實物件,這樣我就能呼叫這組介面中的方法了 
	 * 第三個引數handler, 我們這裡將這個代理物件關聯到了上方的InvocationHandler這個物件上
	 */
	Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);

	System.out.println(subject.getClass().getName());
	subject.rent();
	subject.hello("world");
}

}
文章轉載:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html
spring兩種代理方式
若目標物件實現了若干介面,spring使用JDK的java.lang.reflect.Proxy類代理。
優點:因為有介面,所以使系統更加鬆耦合
缺點:為每一個目標類建立介面
若目標物件沒有實現任何介面,spring使用CGLIB庫生成目標物件的子類。
優點:因為代理類與目標類是繼承關係,所以不需要有介面的存在。
缺點:因為沒有使用介面,所以系統的耦合性沒有使用JDK的動態代理好。