spring aop又是如何实现将增强代码织入到原有方法的业务逻辑里面去的呢?
spring通过jdk dynamic proxy 和 cglib 两种方式,各有优缺,综合使用。
jdk dynamic proxy
jdk dynamic proxy,是创建出目标类的proxy,你在代理类接口约定的方法中去实现自己的增强逻辑,同时告诉代理类目标对象是什么,目标对象的interface,这样代理对象便可根据interface知道目标类有哪些方法,这样代理对象便可通过目标类object和目标类method以及自己作为代理时外部调用输入的调用参数,运用java反射技术调用目标object的method了,在调用目标对象方法前后再适合调用增强逻辑便实现了代码织入了。如下面的例子:
public class ServiceWithPerformanceMonitorProxy implements InvocationHandler {
private Object target;
public ServiceWithPerformanceMonitorProxy(Object target) {
this.target = target;
}
public static Object newProxyInstance(Object target){
Class clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(), new ServiceWithPerformanceMonitorProxy(target));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
PerformanceMonitor.begin(method.getName());
// 通过反射执行被代理原本类的业务代码
Object result = method.invoke(target, args);
PerformanceMonitor.end(method.getName());
return result;
}
}
AdminService target = new AdminServiceImpl();
AdminService proxy = (AdminService) ServiceWithPerformanceMonitorProxy.newProxyInstance(target);
int userId = 10;
String topic = "jdkProxyTest";
proxy.addTopic(userId, topic);
jdk dynamic proxy 是给被代理的目标对象创建了一个代理对象,这个代理对象与目标对象有着相同的接口方法,在方法调用时,代理对象会去适时调用自己额外约定的增强接口方法实现,再通过java反射的方式调用目标对象的原本业务代码,这样我们的监控逻辑和业务逻辑便被代理对象组合到一起了。
cglib
cglib 的实现方式则更直白,不是要织入代码嘛,我直接创建一份新的.class文件,把增强代码和业务代码写到一起不就好了?所以cglib实际上是运用字节码技术,创建了目标类的一个子类,重写了父类的方法,在其中织入了增强逻辑。
public class CGlibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz){
enhancer.setSuperclass(clazz);
// 代理执行时会回调此this持有的intercept方法,以实现代码织入
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object target, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
PerformanceMonitor.begin(method.getName());
Object result = methodProxy.invokeSuper(target, args);
// 下面这样是无法执行原有方法的,因为这里的target并不是原有类的实例,而是代理类的实例
// target : com.dianping.aop.AdminServiceImpl$$EnhancerByCGLIB$$225da297@16dd5a9d
//Object result = method.invoke(target, args);
PerformanceMonitor.end(method.getName());
return result;
}
}
CGlibProxy proxy = new CGlibProxy();
AdminServiceImpl adminService = (AdminServiceImpl) proxy.getProxy(AdminServiceImpl.class);
int userId = 10;
String topic = "cglibProxyEnhanceTest";
adminService.addTopic(userId, topic);
jdk dynamic proxy 执行原本业务逻辑是通过java反射方式调用目标对象的方法,而cglib是通过字节码技术直接生成的目标类的子类,所以只需要执行父类方法就好了,没有反射的过程,所以性能上要远优于jdk dynamic proxy。
但cglib因为要生成字节码,所以在创建代理对象的成本上又要高于jdk dynamic proxy,在spring中如何被代理的bean是scope=”prototype”的话,每次都要生产一个新的对象,那么用cglib就不是那么合适了。不过对于scope=”singleton”的bean来说的话,用cglib创建出来的代理对象执行性能就可以和原本bean的执行性能几乎一致了。
其他
好了,对于spring aop如何织入代码我们就只叨这么多,至于spring是在创建bean的哪个阶段创建出其代理对象的,这个有兴趣可以去了解下BeanPostProcessor这个接口。
另外,spring原本的aop配置方式其实是非常不好用的,所以在spring2.0之后新增了对AspectJ切点表达式的支持,配置起来就非常爽了。但这只是拿来了AspectJ的注解库和解析库,spring aop提供的仍然是有限制的方法级别的切面增强,而AspectJ这个库可以做到语言级别的切面实现,不过呢,做到方法级别也基本够用了,且spring也可以很轻松的集成AspectJ。
AOP的应用场合是受限的,他一般只适合于那些具有横切逻辑的应用场合,如:性能检测、访问控制、事务管理等,但是在这些场景下确实能很好的优雅地无侵入的解决问题,是对OOP的有益补充。