动态代理
JDK动态代理(参考《疯狂Java讲义》(三十六)---- 类加载机制与反射)
JDK动态代理机制可以在运行期间,为相应的接口动态生成对应的代理对象。所以,可以将横切关注点逻辑封装到动态代理的InvacationHandler中,然后在系统运行期间,根据横切关注点需要植入的模块位置,将横切逻辑织入到相应的代理类中。
这种方式实现的唯一缺点是,所有需要织入横切关注点逻辑的模块类都得实现相应的接口,因为动态代理只针对接口有效。
示例:
ForumService.class
package com.ivy.aop; public interface ForumService { void removeTopic(int topicId); void removeForum(int forumId); }
ForumServiceImpl.class
package com.ivy.aop; public class ForumServiceImpl implements ForumService { @Override public void removeTopic(int topicId) { // TODO Auto-generated method stub System.out.println("remove topic record : " + topicId); try { Thread.currentThread().sleep(20); } catch (Exception ex) { throw new RuntimeException(); } } @Override public void removeForum(int forumId) { System.out.println("remove forum record : " + forumId); try { Thread.currentThread().sleep(40); } catch (Exception ex) { throw new RuntimeException(); } } }
PerformanceMonitor.class
package com.ivy.aop; public class PerformanceMonitor { private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>(); public static void begin(String method) { System.out.println("begin monitor..."); MethodPerformance mp = new MethodPerformance(method); performanceRecord.set(mp); } public static void end() { System.out.println("end monitor..."); MethodPerformance mp = performanceRecord.get(); mp.printPerformance(); } }
MethodPerformance.class
package com.ivy.aop; public class MethodPerformance { private long begin; private long end; private String serviceMethod; public MethodPerformance(String serviceMethod) { this.serviceMethod = serviceMethod; this.begin = System.currentTimeMillis(); } public void printPerformance() { end = System.currentTimeMillis(); long elapse = end - begin; System.out.println(serviceMethod + " cost " + elapse + "ms."); } }
PerformanceHandler.class
package com.ivy.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PerformanceHandler implements InvocationHandler{
private Object target;
public PerformanceHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
PerformanceMonitor.begin(target.getClass().getName() + "." + method.getName());
Object object = method.invoke(target, args);
PerformanceMonitor.end();
return object;
}
}
TestForumServiceByJDKProxy.class
package com.ivy.aop;
import java.lang.reflect.Proxy;
public class TestForumServiceByJDKProxy {
public static void main(String[] args) {
ForumService target = new ForumServiceImpl();
PerformanceHandler handler = new PerformanceHandler(target);
// 只能对接口进行代理
ForumService proxy = (ForumService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
proxy.removeForum(10);
proxy.removeTopic(1012);
}
}
运行结果:
begin monitor... remove forum record : 10 end monitor... com.ivy.aop.ForumServiceImpl.removeForum cost 41ms. begin monitor... remove topic record : 1012 end monitor... com.ivy.aop.ForumServiceImpl.removeTopic cost 20ms.
动态字节码增强 -- CGLib动态代理
java虚拟机加载的class文件都是符合一定规范的,所以,只要交给虚拟机运行的文件符合Java Class规范,程序的运行就么有问题。通常的class文件都是从Java源代码文件使用Javac编译器编译而成的,但只要符合Java Class规范,我们也可以使用CGLib等Java工具类,在程序运行期间,动态构建字节码的class文件。在这样的前提下,我们可以为需要织入横切逻辑的模块类在运行期间,通过动态字节码增强技术,为这些系统模块类生成相应的子类,而将横切逻辑加到这些子类中,让应用程序在执行期间使用的是这些动态生成的子类,从而达到将横切逻辑织入系统的目的。使用动态字节码增强技术,即使模块类没有实现相应的接口,我们依然可以对其进行扩展,而不用像动态代理那样受限于接口。不过这种实现的不足是,如果需要扩展的类以及类中的实例方法等声名为final,则无法对其进行子类化扩展。
Spring AOP在无法使用动态代理机制进行AOP扩展的时候,会使用CGLIB库德动态字节码增强来实现AOP的功能扩展。
使用JDK创建代理有一个限制,它只能够为接口创建代理实例,对于没有通过接口定义业务方法的类,就需要CGLib动态创建代理了。
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有弗雷方法的调用,并顺势织入横切逻辑。
CglibProxy.class
package com.ivy.aop;
import java.lang.reflect.Method;
import org.springframework.aop.interceptor.PerformanceMonitorInterceptor;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor{
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
// 设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
// 通过字节码结束动态创建子类实例
return enhancer.create();
}
@Override
// 拦截父类所有方法的调用
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
PerformanceMonitor.begin(obj.getClass().getName() + "." + method.getName());
// 通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
PerformanceMonitor.end();
return result;
}
}
TestForumService.class
package com.ivy.aop;
public class TestForumService {
public static void main(String[] args) {
// TODO Auto-generated method stub
CglibProxy proxy = new CglibProxy();
// 可以对实现类进行代理
ForumServiceImpl forumService = (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);
}
}
运行结果:
begin monitor...
remove forum record : 10
end monitor...
com.ivy.aop.ForumServiceImpl$$EnhancerByCGLIB$$2d2c5cf3.removeForum cost 41ms.
begin monitor...
remove topic record : 1023
end monitor...
com.ivy.aop.ForumServiceImpl$$EnhancerByCGLIB$$2d2c5cf3.removeTopic cost 20ms.