前言
嗯,我應該是有一段實現沒有寫過博客了,在寫完了細說Spring——AOP詳解(AOP概覽)之后,我發現我不知道該怎么寫AOP這一部分,所以就把寫博客這件事給放下了,但是這件事情又不想就這么放棄,所以今天我仔細思考了一下,決定還是要克服困難,我仔細的想了一下怎么講解AOP實現這一部分,然后我決定由淺入深的講解動態代理,然后用動態代理實現一個簡單的AOP,感覺這樣能夠讓人對AOP的原理有一個比較深刻的認識,希望能幫到大家。而且最近學習又組建了ACM比賽的隊伍,雖然已經要大三了,按理來說應該一心學習Java Web的開發,可是我對算法也有一定的興趣,并且之前也確實花過一段時間練習算法題,所以還是決定要參加一下,所以寫博客可能就會慢一點吧。
一、什么是動態代理
動態代理其實就是Java
中的一個方法,這個方法可以實現:動態創建一組指定的接口的實現對象(在運行時,創建實現了指定的一組接口的對象)
這里聲明一下,本篇博客中會使用很多AOP中的術語,所以如果看不懂術語的話一定要先看一下細說Spring——AOP詳解(AOP概覽)
例如:
interface A {}
interface B {}
//obj對象的類型實現了A和B兩個接口
Object obj = 方法(new Class[]{A.class, B.class})
二、動態代理初體驗
我們根據上面的思路來體驗一下Java中的動態代理吧,首先我們要先寫兩個接口。
interface A {
public void a();
}
interface B {
public void b();
}
然后我們就先來看一下動態代理的代碼:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
上面這個就是動態代理類(Proxy)類中的創建代理對象的方法,下面介紹一下方法的三個參數:
-
ClassLoader loader
:方法需要動態生成一個類,這個類實現了A和B兩個接口,然后創建這個類的對象。需要生成一個類,而且這個類也需要加載到方法區中,所以我們需要一個ClassLoader
來加載該類 -
Class<?>[] interfaces
:我們需要代理對象實現的數組 -
InvocationHandler h
:調用處理器
這里你可能對InvocationHandler
有疑惑,這里先買個關子,下面馬上揭曉。
我們現在就使用動態代理創建一個代理對象吧。
@Test
public void test1() {
/**
* 三個參數
* 1、ClassLoader
* 方法需要動態生成一個類,這個類實現了A和B兩個接口,然后創建這個類的對象
* 需要生成一個類,這個類也需要加載到方法區中,所以我們需要一個ClassLoader來加載該類
*
* 2、Class[] interfaces
* 我們需要代理對象實現的數組
*
* 3、InvocationHandler
* 調用處理器
*/
ClassLoader classLoader = this.getClass().getClassLoader();
//這里創建一個空實現的調用處理器。
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
};
Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler);
//強轉為A和B接口類型,說明生成的代理對象實現了A和B接口
A a = (A) obj;
B b = (B) obj;
}
經過測試代碼運行成功,說明生成的代理對象確實實現了A接口和B接口,但是我想你一定會對代理對象如何實現了A接口和B接口感興趣,你一定想知道如果使用代理對象調用相應接口的方法會發生什么感興趣,下面我們一起來探究一下:
上面代碼的基礎上加上下面的代碼
a.a();
b.b();
我們可以發現什么也沒有發生。這是因為我們根本沒有為代理對象添加實現邏輯。可是實現邏輯添加在哪里呢?哈哈,當然是
InvocationHandler
中了。下面就看一看添加了實現邏輯的代碼:
@Test
public void test2() {
/**
* 三個參數
* 1、ClassLoader
* 方法需要動態生成一個類,這個類實現了A和B兩個接口,然后創建這個類的對象
* 需要生成一個類,這個類也需要加載到方法區中,所以我們需要一個ClassLoader來加載該類
*
* 2、Class[] interfaces
* 我們需要代理對象實現的數組
*
* 3、InvocationHandler
* 調用處理器
*
* 代理對象實現的所有接口中的方法,內容都是調用InvocationHandler的invoke()方法
*/
ClassLoader classLoader = this.getClass().getClassLoader();
//這里創建一個空實現的調用處理器。
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("你好!!!!");//注意這里添加了一點小邏輯
return null;
}
};
Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler);
//強轉為A和B接口類型,說明生成的代理對象實現了A和B接口
A a = (A) obj;
B b = (B) obj;
a.a();
b.b();
}
截圖如下:
這里我們發現A接口和B接口的實現邏輯都是調用了
invoke
這個方法中的邏輯,其實除了調用代理對象的native
方法,調用代理對象的其他所有方法本質都是調用了invoke
方法,下面我們再來看第三個實例,讓我們對動態代理有更深刻的認識。
public void test3() {
/**
* 三個參數
* 1、ClassLoader
* 方法需要動態生成一個類,這個類實現了A和B兩個接口,然后創建這個類的對象
* 需要生成一個類,這個類也需要加載到方法區中,所以我們需要一個ClassLoader來加載該類
*
* 2、Class[] interfaces
* 我們需要代理對象實現的數組
*
* 3、InvocationHandler
* 調用處理器
*
* 代理對象實現的所有接口中的方法,內容都是調用InvocationHandler的invoke()方法
*/
ClassLoader classLoader = this.getClass().getClassLoader();
//這里創建一個空實現的調用處理器。
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("你好!!!!");
return "Hello";//這里改為返回"Hello"
}
};
Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler);
//強轉為A和B接口類型,說明生成的代理對象實現了A和B接口
A a = (A) obj;
B b = (B) obj;
a.toString();//注意這里調用了toString()方法
b.getClass();//注意這里調用了getClass()方法
//這里在A接口中添加了一個方法public Object aaa(String s1, int i);
Object hello = a.aaa("Hello", 100);
System.out.println(obj.getClass());//這里看一下代理對象是什么
System.out.println(hello);//這里看一下返回值是什么
}
通過代碼的結果我們大膽的猜測一下,代理對象方法的返回值其實就是
invoke
方法的返回值,代理對象其實就是使用反射機制實現的一個運行時對象。哈哈,當然這些肯定不是猜測了,其實確實就是這樣。下面是時候總結一下InvocationHandler
的invoke
方法了。如下圖所示:當我們調用代理對象的方法時,其對應關系就如上圖所示。
三、初步實現AOP
在我們對動態代理有了一定的認識之后,我們就可以實現最基本版本的AOP了,當然,這是一個非常殘缺的AOP實現,甚至都不能稱之為AOP實現。
我們先寫一個接口:
package demo2;
/**
* Created by Yifan Jia on 2018/6/5.
*/
//服務生
public interface Waiter {
//服務方法
public void server();
}
然后給出該接口的實現類:
package demo2;
/**
* Created by Yifan Jia on 2018/6/5.
*/
public class ManWaiter implements Waiter {
@Override
public void server() {
System.out.println("服務中");
}
}
然后我們就通過動態代理來對上面的ManWaiter
進行增強:
package demo2;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by Yifan Jia on 2018/6/5.
*/
public class Demo2 {
@Test
public void test1() {
Waiter waiter = new ManWaiter();
waiter.server();
}
@Test
public void test2() {
Waiter manWaiter = new ManWaiter();
ClassLoader classLoader = this.getClass().getClassLoader();
Class[] interfaces = {Waiter.class};
InvocationHandler invocationHandler = new WaiterInvocationHandler(manWaiter);
//得到代理對象,代理對象就是在目標對象的基礎上進行了增強的對象
Waiter waiter = (Waiter) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
waiter.server();//前面添加“你好”,后面添加“再見”
}
}
class WaiterInvocationHandler implements InvocationHandler {
private Waiter waiter;
WaiterInvocationHandler(Waiter waiter) {
this.waiter = waiter;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("你好");
waiter.server();//調用目標對象的方法
System.out.println("再見");
return null;
}
}
結果如下:
你肯定要說了,這算什么AOP,增強的代碼都是硬編碼到
invoke
方法中的,大家稍安勿躁,我們不是已經對需要增強的對象做了增強嗎。這里可以的目標對象為manWaiter
,增強為System.out.println("你好");
和System.out.println("再見");
,切點為server()
方法調用。其實還是可以看做一下原始的AOP的。
四、完善的AOP實現
我們從初步實現的AOP中可以發現很多問題,比如我們不能把增強的邏輯硬編碼到代碼中,我們需要實現可變的增強,下面我們就解決一下這些問題,來實現一個比較完善的AOP。
我們仍然引用上面的Waiter
接口和Manwaiter
實現類。
然后我們添加一個前置增強接口:
/**
* 前置增強
*/
public interface BeforeAdvice {
public void before();
}
再添加一個后置增強接口:
public interface AfterAdvice {
public void after();
}
我們把產生代理對象的代碼封裝為一個類:
package demo3;
import com.sun.org.apache.regexp.internal.RE;
import org.junit.After;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* ProxFactory用來生成代理對象
* 它需要所有的參數:目標對象,增強,
* Created by Yifan Jia on 2018/6/5.
*/
/**
* 1、創建代理工廠
* 2、給工廠設置目標對象、前置增強、后置增強
* 3、調用creatProxy()得到代理對象
* 4、執行代理對象方法時,先執行前置增強,然后是目標方法,最后是后置增強
*/
//其實在Spring中的AOP的動態代理實現的一個織入器也是叫做ProxyFactory
public class ProxyFactory {
private Object targetObject;//目標對象
private BeforeAdvice beforeAdvice;//前值增強
private AfterAdvice afterAdvice;//后置增強
/**
* 用來生成代理對象
* @return
*/
public Object creatProxy() {
/**
* 給出三個參數
*/
ClassLoader classLoader = this.getClass().getClassLoader();
//獲取當前類型所實現的所有接口類型
Class[] interfaces = targetObject.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 在調用代理對象的方法時,會執行這里的內容
*/
if(beforeAdvice != null) {
beforeAdvice.before();
}
Object result = method.invoke(targetObject, args);//調用目標對象的目標方法
//執行后續增強
afterAdvice.after();
//返回目標對象的返回值
return result;
}
};
/**
* 2、得到代理對象
*/
Object proxyObject = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return proxyObject;
}
//get和set方法略
}
然后我們將相關的參數注入到ProxyFactory
后就可以通過creatProxy()
方法獲取代理對象了,代碼如下:
package demo3;
import org.junit.Test;
/**
* Created by Yifan Jia on 2018/6/5.
*/
public class Demo3 {
@Test
public void tset1() {
ProxyFactory proxyFactory = new ProxyFactory();//創建工廠
proxyFactory.setTargetObject(new ManWaiter());//設置目標對象
//設置前置增強
proxyFactory.setBeforeAdvice(new BeforeAdvice() {
@Override
public void before() {
System.out.println("客戶你好");
}
});
//設置后置增強
proxyFactory.setAfterAdvice(new AfterAdvice() {
@Override
public void after() {
System.out.println("客戶再見");
}
});
Waiter waiter = (Waiter) proxyFactory.creatProxy();
waiter.server();
}
}
結果如下:
這時候我們已經可以自定義任意的增強邏輯了,是不是很神奇。
五、動態代理實現AOP總結
通過上面的內容,我們已經通過動態代理實現了一個非常簡陋的AOP,這里的AOP實現還是有很多的不足之處。下面我把Spring中的ProxyFactory
實現貼出來,大家可以研究一下Spring中的ProxyFactory
的優勢在哪里,另外,Spring中還有其他的基于動態代理實現的織入器,ProxyFactory
只是其中最基礎的版本,大家有興趣可以研究一下。
public class ProxyFactory extends ProxyCreatorSupport {
public ProxyFactory() {
}
public ProxyFactory(Object target) {
Assert.notNull(target, "Target object must not be null");
this.setInterfaces(ClassUtils.getAllInterfaces(target));
this.setTarget(target);
}
public ProxyFactory(Class... proxyInterfaces) {
this.setInterfaces(proxyInterfaces);
}
public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {
this.addInterface(proxyInterface);
this.addAdvice(interceptor);
}
public ProxyFactory(Class<?> proxyInterface, TargetSource targetSource) {
this.addInterface(proxyInterface);
this.setTargetSource(targetSource);
}
public Object getProxy() {
return this.createAopProxy().getProxy();
}
public Object getProxy(ClassLoader classLoader) {
return this.createAopProxy().getProxy(classLoader);
}
public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {
return (new ProxyFactory(proxyInterface, interceptor)).getProxy();
}
public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) {
return (new ProxyFactory(proxyInterface, targetSource)).getProxy();
}
public static Object getProxy(TargetSource targetSource) {
if(targetSource.getTargetClass() == null) {
throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");
} else {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(targetSource);
proxyFactory.setProxyTargetClass(true);
return proxyFactory.getProxy();
}
}
}