使用 cglib 實現簡單的 aop 模塊
around 還有些問題。
jar 包#
需要 jar 包cglib-nodep-2.1_3
,或者 maven 加上
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
版本視情況改變。
目標類#
假設有這麼個目標類型
public class TestTarget {
public void out(String a) {
System.out.println("Target out."+a);
}
}
TestTarget 類裡面有個out
方法,我們希望在out
方法執行的時候打 log。
增強器#
增強器 Enhancer 可以增強原有的方法,並返回一個新的對象。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestTarget.class);
enhancer.setCallback(callback);
代碼創建一個 enhancer,然後把一個Class<TestTarget>
對象傳進去,enhancer 對它幹了什麼,暫時不用理。
隨後傳入一個 callback,callback 是這樣的:
public class BeforeCallBack implements MethodInterceptor {
public BeforeCallBack() {
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// do something here.
return methodProxy.invokeSuper(o, objects);
}
}
BeforeCallBack
實現了MethodInterceptor
接口。這裡才是真正的增強,在 return 原有的方法之前,執行一些東西(這裡是 before 切點,也可以改成 after、around)
效果:
此處匿名內部類實現的接口並替換成 lambda。
public class MainClass {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestTarget.class);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("before.");
return methodProxy.invokeSuper(o,objects);
});
TestTarget testTarget = (TestTarget) enhancer.create();
testTarget.out();
}
}
class TestTarget{
public void out(){
System.out.println("out. ");
}
}
before.
out.
通用化#
- 切點
在增強的時候,我們並沒有指定是哪一個方法被增強,所以類裡面的所有方法都會被起作用。
cglib 提供了CallbackFilter
來區分什麼方法應該使用什麼 Callback(before、after..etc)來處理。
用來標記方法類型的,註解@interface
無疑是最合適的了。
- 切面
我們在攔截了方法之後,在它返回前後進行一些操作,在intercept
方法裡邊寫上我們要有的操作。
也就是說,每有一個 Callback 的需求,我們都要手動的實現接口並且加上業務代碼。那麼我們應該把業務代碼抽取出來。
把業務抽象成一個切面,比如打 log,我們有個 LogAspect, 繼承於 AbstractAspect,屆時,只要在intercept
跑抽象的方法就可以了。
比如:
public class BeforeCallBack implements MethodInterceptor {
Class<? extends AbstractAspect> aspect;
public BeforeCallBack(Class<? extends AbstractAspect> aspect) {
this.aspect = aspect;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
AbstractAspect instance = aspect.newInstance();
instance.doBefore();
return methodProxy.invokeSuper(o, objects);
}
}
不管實際上的切面是什麼,他都是AbstractAspect
。
- 註解解析
主要的註解有兩個:Aspect
、PointCut
, 一個是用於標記 TargetClass 要應用什麼切面,一個是用於標記方法的切點類型(before、after..)