AOP是具有特定的应用场合的,它只适合那些具有横切逻辑的应用场合,如性能检测、访问控制、事务管理及日志纪录。
Spring AOP使用动态代理技术在运行期织入增强的代码,Spring AOP使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。
下面我们通过一个带有横切逻辑的实例来实现这两种代理方式:
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();
}
}
测试代码:
ForumService forumService = new ForumServiceImpl();
forumService.removeForum(10);
forumService.removeTopic(1012);
上面代码每个方法执行前后都加入了性能检测代码,这是完美的横切逻辑。
JDK动态代理
Java1.3后,Java提供了动态代理技术,运行开发者在运行期间创建接口的代理实例。JDK动态代理主要涉及java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。
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;
}
}
测试代码:
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动态代理
CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。
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;
}
}
测试代码:
CglibProxy cglibProxy = new CglibProxy();
ForumService forumService = (ForumService)cglibProxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);
需要注意的是,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final或private方法进行代理。
为什么会有两种代理机制
- JDK创建代理有一个限制,即它只能为接口创建代理实例,虽然面向接口编程是好的编程习惯,但有时候并不是必须的,这是JDK动态代理的局限性。
- 就性能来说,CGLib所创建的动态代理对象的性能比JDK所创建的动态代理对象的性能高差不多10倍,CGLib在创建代理对象时所话费的时间却比JDK动态代理大概多8倍,但是对于singleton的代理对象或者具有实例池的代理,因为无需频繁创建代理对象,所以比较合适采用CGLib动态代理技术,反之则适合采用JDK动态代理技术。