AOP是具有特定的應(yīng)用場(chǎng)合的钳恕,它只適合那些具有橫切邏輯的應(yīng)用場(chǎng)合炼彪,如性能檢測(cè)哄辣、訪問控制请梢、事務(wù)管理及日志紀(jì)錄。
Spring AOP使用動(dòng)態(tài)代理技術(shù)在運(yùn)行期織入增強(qiáng)的代碼力穗,Spring AOP使用了兩種代理機(jī)制:一種是基于JDK的動(dòng)態(tài)代理毅弧;另一種是基于CGLib的動(dòng)態(tài)代理。
下面我們通過一個(gè)帶有橫切邏輯的實(shí)例來實(shí)現(xiàn)這兩種代理方式:
public class ForumServiceImpl implements ForumService {
public void removeTopic(int topicId) {
PerformanceMonitor.begin("com.smart.proxy.ForumServiceImpl.removeTopic");
System.out.println("模擬刪除Topic記錄:"+topicId);
try {
Thread.sleep(20);
} catch (Exception e) {
throw new RuntimeException(e);
}
PerformanceMonitor.end();
}
public void removeForum(int forumId) {
PerformanceMonitor.begin("com.smart.proxy.ForumServiceImpl.removeForum");
System.out.println("模擬刪除Forum記錄:"+forumId);
try {
Thread.sleep(40);
} catch (Exception e) {
throw new RuntimeException(e);
}
PerformanceMonitor.end();
}
}
測(cè)試代碼:
ForumService forumService = new ForumServiceImpl();
forumService.removeForum(10);
forumService.removeTopic(1012);
上面代碼每個(gè)方法執(zhí)行前后都加入了性能檢測(cè)代碼当窗,這是完美的橫切邏輯形真。
JDK動(dòng)態(tài)代理
Java1.3后,Java提供了動(dòng)態(tài)代理技術(shù)超全,運(yùn)行開發(fā)者在運(yùn)行期間創(chuàng)建接口的代理實(shí)例咆霜。JDK動(dòng)態(tài)代理主要涉及java.lang.reflect包中的兩個(gè)類:Proxy和InvocationHandler。InvocationHandler是一個(gè)接口嘶朱,可以通過實(shí)現(xiàn)該接口定義橫切邏輯蛾坯,并通過反射機(jī)制調(diào)用目標(biāo)類的代碼,動(dòng)態(tài)地將橫切邏輯和業(yè)務(wù)邏輯編織在一起疏遏。
public class PerformaceHandler implements InvocationHandler {
private Object target;
public PerformaceHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
PerformanceMonitor.begin(target.getClass().getName() + "." + method.getName());
Object obj = method.invoke(target, args);
PerformanceMonitor.end();
return obj;
}
}
測(cè)試代碼:
ForumService target = new ForumServiceImpl();
PerformaceHandler handler = new PerformaceHandler(target);
ForumService proxy = (ForumService) Proxy.newProxyInstance(target
.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
proxy.removeForum(10);
proxy.removeTopic(1012);
CGLib動(dòng)態(tài)代理
CGLib采用底層的字節(jié)碼技術(shù)脉课,可以為一個(gè)類創(chuàng)建子類救军,在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用并順勢(shì)織入橫切邏輯。
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName());
Object result=proxy.invokeSuper(obj, args);
PerformanceMonitor.end();
return result;
}
}
測(cè)試代碼:
CglibProxy cglibProxy = new CglibProxy();
ForumService forumService = (ForumService)cglibProxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);
需要注意的是倘零,由于CGLib采用動(dòng)態(tài)創(chuàng)建子類的方式生成代理對(duì)象唱遭,所以不能對(duì)目標(biāo)類中的final或private方法進(jìn)行代理。
為什么會(huì)有兩種代理機(jī)制
- JDK創(chuàng)建代理有一個(gè)限制呈驶,即它只能為接口創(chuàng)建代理實(shí)例拷泽,雖然面向接口編程是好的編程習(xí)慣,但有時(shí)候并不是必須的袖瞻,這是JDK動(dòng)態(tài)代理的局限性司致。
- 就性能來說,CGLib所創(chuàng)建的動(dòng)態(tài)代理對(duì)象的性能比JDK所創(chuàng)建的動(dòng)態(tài)代理對(duì)象的性能高差不多10倍聋迎,CGLib在創(chuàng)建代理對(duì)象時(shí)所話費(fèi)的時(shí)間卻比JDK動(dòng)態(tài)代理大概多8倍脂矫,但是對(duì)于singleton的代理對(duì)象或者具有實(shí)例池的代理,因?yàn)闊o需頻繁創(chuàng)建代理對(duì)象霉晕,所以比較合適采用CGLib動(dòng)態(tài)代理技術(shù)庭再,反之則適合采用JDK動(dòng)態(tài)代理技術(shù)。