AOP的实现者:AspectJ、JBoss AOP、Spring AOP,其中AspectJ提供语言级别的AOP实现,有一个专门的编译器生成遵守Java字节编码规范的Class文件。在Spring中,可以无缝集成Spring AOP、IoC、AspectJ。
Spring AOP使用了两种代理机制:一种是基于JDK的动态代理,另一种是基于CGLib的动态代理,之所以需要两种代理机制,很大程度上是因为JDK本身只提供接口的代理,而不支持类的代理。
》JDK动态代理实例:
package com.springzoo.proxy;
public interface ForumService {
void removeTopic(int topicId);
void removeForum(int forumId);
}
package com.springzoo.proxy;
public class ForumServiceImpl implements ForumService {
public void removeTopic(int topicId) {
System.out.println("模拟删除Topic记录:"+topicId);
try {
Thread.currentThread().sleep(20);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void removeForum(int forumId) {
System.out.println("模拟删除Forum记录:"+forumId);
try {
Thread.currentThread().sleep(40);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
package com.springzoo.proxy;
public class PerformanceMonitor {
private static ThreadLocal<MethodPerformace> performaceRecord = new ThreadLocal<MethodPerformace>();
public static void begin(String method) {
System.out.println("begin monitor...");
MethodPerformace mp = performaceRecord.get();
if(mp == null){
mp = new MethodPerformace(method);
performaceRecord.set(mp);
}else{
mp.reset(method);
}
}
public static void end() {
System.out.println("end monitor...");
MethodPerformace mp = performaceRecord.get();
mp.printPerformace();
}
}
package com.springzoo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
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;
}
}
最后测试下:
package com.springzoo.proxy;
public class TestForumService {
public static void main(String[] args) {
// 使用JDK动态代理
// 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动态代理
CglibProxy proxy = new CglibProxy();
ForumService forumService = (ForumService)proxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);
}
}
以上的手动编写AOP逻辑和植入非常的不方便,而spring AOP就是要解决这个问题。spring AOP通过Pointcut指定在哪些类的哪些方法上织入横切逻辑,通过Advice描述横切逻辑和方法的织入方位(方法前、方法后、方法前后,跑出异常时)。此外,spring通过Advisor切面将Pointcut和Advice两者组装起来。有了Advisor的信息,spring就可以利用JDK或者CGLib的动态代理技术采用统一的方式为目标Bean创建织入切面的代理对象了。
动态代理性能:CGLib的代理对象性能是JDK的10倍,但是创建时间是JDK的8倍,因此对于singleton的对象适合用CGLib,而其他的情况适合用JDK动态代理。注意,CGLib由于采用动态创建子类方式生产代理对象,所以不能对目标类中的final方法进行代理。
》Introduction,引介增强
引介增强可以为目标类创建新的方法和属性,所以其连接点是类级别,通过引介增强我们可以为目标类添加一个接口的实现,通过引介增强我们可以为目标类创建实现某接口的代理。继承DelegatingIntroductionInterceptor即可,覆盖其中的invoke方法。
先定义一个接口类:
package com.springzoo.introduce;
public interface Monitorable {
void setMonitorActive(boolean active);
}
然后自定义Introduction:
package com.springzoo.introduce;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
public class ControllablePerformaceMonitor extends DelegatingIntroductionInterceptor
implements Monitorable{
private ThreadLocal<Boolean> monitorStatusMap = new ThreadLocal<Boolean>();
public void setMonitorActive(boolean active) {
monitorStatusMap.set(active);
}
public Object invoke(MethodInvocation mi) throws Throwable {
Object obj = null;
if (monitorStatusMap.get() != null && monitorStatusMap.get()) {
PerformanceMonitor.begin(mi.getClass().getName() + "."
+ mi.getMethod().getName());
obj = super.invoke(mi);
PerformanceMonitor.end();
} else {
obj = super.invoke(mi);
}
return obj;
}
}
接下来在spring中配置Introduction:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="pmonitor" class="com.springzoo.introduce.ControllablePerformaceMonitor" />
<bean id="forumServiceTarget" class="com.springzoo.introduce.ForumService" />
<bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interfaces="com.springzoo.introduce.Monitorable"
p:target-ref="forumServiceTarget"
p:interceptorNames="pmonitor"
p:proxyTargetClass="true" />
</beans>
注意:必须指定引介增强所实现的接口,p:inaterfaces,其次由于只能通过为目标类创建子类方式生产引介增强代理,所以必须将proxyTargetClass设为true。还有就是我们没有对ControllerPerformanceMonitor进行线程安全的特殊处理,就必须将singleton属性设为true让ProxyFactoryBean产生prototype的代理,这样会带来严重的性能问题,因为CGLib创建动态代理的性能很低,所以加了个TreadLocal,那么就可以了。
》创建切面Advisor
我们再次给出spring AOP如何定位连接点:
增强Advice提供了连接点方位信息:如织入方法前、后等,而切点进一步描述织入到哪些类的哪些方法上。
spring通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成。
本人博客已搬家,新地址为:http://yidao620c.github.io/