Proxy和InvocationHandler實現自己的AOP
一、
spring Aop實際上也是通過動態代理機制進行面向切面程式設計的,在java裡可以通過實現InvocationHandler介面建立自己的動態代理類,然後通過Proxy建立動態的代理類的物件。
動態代理其實就是java.lang.reflect.Proxy類動態的根據指定的所有介面生成一個class byte,該class會繼承Proxy類,並實現所有你指定的介面(您在引數中傳入的介面陣列);然後再利用指定的classloader將 class byte載入進系統,最後生成這樣一個類的物件,並初始化該物件的一些值,如invocationHandler,以即所有的介面對應的Method成員。 初始化之後將物件返回給呼叫的客戶端。這樣客戶端拿到的就是一個實現你所有的介面的Proxy物件
首先InvocationHandler介面中只有一個方法
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我們所代理的那個真實物件
method: 指代的是我們所要呼叫真實物件的某個方法的Method物件
args: 指代的是呼叫真實物件某個方法時接受的引數
Proxy中動態建立例項的方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
這個方法的作用就是得到一個動態的代理物件,其接收三個引數,我們來看看這三個引數所代表的含義:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader: 一個ClassLoader物件,定義了由哪個ClassLoader物件來對生成的代理物件進行載入
interfaces: 一個Interface 物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了
h: 一個InvocationHandler物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上
二、
好了現在簡單的模擬一下aop
首先新建一個介面
public interface Subject {
public void rent();
public void hello(String str);
}
對應的實現類
public class SubjectImpl 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);
}
}
public class DynamicProxy implements InvocationHandler
{
// 要代理的物件
private Object target;
// 將構造方法禁用掉,不讓外部通過new來得到DynamicProxy物件
private DynamicProxy()
{
};
/**
* 返回一個動態的代理物件
*
* @param object
* @return
*/
public static Object newInstance(Object object)
{
DynamicProxy proxy = new DynamicProxy();
proxy.target = object;
// 通過Proxy的newProxyInstance方法來得到一個代理物件
Object result = Proxy.newProxyInstance(object.getClass()
.getClassLoader(), object.getClass().getInterfaces(), proxy);
return result;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
// // 只有方法名為add和delete時候才引入日誌
// if(method.getName().equals("add") || method.getName().equals("delete"))
// {
// Logger.logInfo("動態代理類");
// }
// 根據LogAnnotation來判斷,如果被標註了註解,則輸出日誌
if(method.isAnnotationPresent(LogAnnotation.class))
{
LogAnnotation log = method.getAnnotation(LogAnnotation.class);
Logger.logInfo(log.value());
}
Object object = method.invoke(target, args);
return object;
}
}
然後是spring的配置
<!-- 如果要對static方法進行注入,可以通過factory-method屬性來制定方法名字,並通過建構函式的方式傳入引數 -->
<bean id="userDAOProxy" class="com.xiaoluo.proxy.DynamicProxy" factory-method="newInstance">
<constructor-arg ref="userDAO"/>
</bean>
因為我們的DynamicProxy類的物件以及代理物件是通過static方法來進行注入的,因此我們如果要對其進行注入的話,需要通過 factory-method 這個屬性來給我們的靜態方法進行屬性注入,通過 來講引數傳遞進去,這樣我們的userDAOProxy就是一個代理物件了
三、
關於InvocationHandler介面的invoke方法,其實這個類就是最終Proxy呼叫的固定介面方法。Proxy不管客戶端的業務方法是怎麼實現的。當客戶端呼叫Proxy時,它只
會呼叫InvocationHandler的invoke介面,所以我們的真正實現的方法就必須在invoke方法中去呼叫。關係如下:
BusinessProcessorImpl bpimpl = new BusinessProcessorImpl();
BusinessProcessorHandler handler = new BusinessProcessorHandler(bpimpl);
BusinessProcessor bp = (BusinessProcessor)Proxy.newProxyInstance(....);
bp.processBusiness()-->invocationHandler.invoke()-->bpimpl.processBusiness();
Proxy.newProxyInstance方法會做如下幾件事:
1,根據傳入的第二個引數interfaces動態生成一個類,實現interfaces中的介面,該例中即BusinessProcessor介面的processBusiness方法。並且繼承了Proxy類,重寫了hashcode,toString,equals等三個方法。具體實現可參看 ProxyGenerator.generateProxyClass(…); 該例中生成了$Proxy0類
2,通過傳入的第一個引數classloder將剛生成的類載入到jvm中。即將$Proxy0類load
3,利用第三個引數,呼叫
4,將$Proxy0的例項返回給客戶端。
現在好了。我們再看客戶端怎麼調就清楚了。
1,客戶端拿到的是
BusinessProcessor bp = (BusinessProcessor)Proxy.newProxyInstance(….);
2,bp.processBusiness();
實際上呼叫的是
注:文章以自己記錄為主,詳細的可以去下面地方看