AOP,久违了!
在AOP概念理解里面呢,学习了一下AOP的一些使用场合,这一篇呢,就来真正写一些示例,来学习这样的一个概念。
在Spring AOP中呢,使用了动态代理技术在运行期织入增强的代码,我们都知道,JDK动态代理只提供了针对接口的代理,CGLib是对其的强有力补充,Spring AOP支持这两种代理方式。
先来看一个一般的带有AOP概念色彩的示例程序,然后再在这个程序的基础上,使用上JDK动态代理技术和CGlib代理技术。
1)
public class MethodPerformance { private static final Log log = LogFactory.getLog(MethodPerformance.class); private long begin; private long end; private String serviceMethod;//执行的方法 public MethodPerformance(String serviceMethod){ this.serviceMethod = serviceMethod; this.begin = System.currentTimeMillis(); } public void printPerformance(){ this.end = System.currentTimeMillis(); log.info(serviceMethod+" processing take "+(end-begin)+" millisecond.");//方法执行的耗时 } }
这个类完成的功能是这样的,就是来监测一个类中方法的执行耗时。这样一来,可以有效的监测程序的执行耗时,从而为程序的运行速度做一些参考。
PerformanceMonitoring这个类呢,是对当前线程中运行的方法进行一个监测,实际上它充当了MethodPerformance的包装器了,begin方法接受执行的方法名,并记录方法执行的起始时间,end方法记录该方法的执行耗时,程序和逻辑都是相当的简单明了,现代码如下:
public class PerformanceMonitoring { private static final Log log = LogFactory.getLog(PerformanceMonitoring.class); private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>();//当前线程中 public static void begin(String method){ log.info("begin monitoring..."); MethodPerformance mp = new MethodPerformance(method); performanceRecord.set(mp); } public static void end(){ log.info("end monitoring..."); MethodPerformance mp = performanceRecord.get();//获取当前线程的对象实例 mp.printPerformance(); } }
现看一个service类,看看如何在方法中使用监测耗时,代码如下:
public class AopStudyService extends TestCase{ private static final AopStudyService instance = new AopStudyService(); public static AopStudyService getInstance(){ return instance; } public void analogRemoveInfo() throws InterruptedException{ PerformanceMonitoring.begin("analogRemoveInfo");//性能监测 Thread.currentThread().sleep(200);//模拟该方法执行了200毫秒 PerformanceMonitoring.end();//结束性能监测 } public void testAnalogRemoveInfo() throws InterruptedException{ instance.analogRemoveInfo(); } }
analogRemoveInfo方法,模拟了一个耗时200ms的删除操作,通过PerformanceMonitoring.begin("analogRemoveInfo")来启动监测起始方法,方法执行完毕后,再调用PerformanceMonitoring.end()来结束监测,最后我们用JUnit Test运行,结果输出如下:
2010-11-21 21:11:21 org.aopStudy.common.PerformanceMonitoring begin 信息: begin monitoring... 2010-11-21 21:11:22 org.aopStudy.common.PerformanceMonitoring end 信息: end monitoring... 2010-11-21 21:11:22 org.aopStudy.common.MethodPerformance printPerformance 信息: analogRemoveInfo processing take 203 millisecond.
通过这个例子,我想你肯定会对aop有一个感性的认识,但是这个程序有一个问题,我现在service里只有一个方法,如果service里的方法多了,那岂不是每个方法的执行开始之前我都要加入begin方法,方法执行完毕后调用end方法,这真的是件糟糕的事情,这无形中添加了我们写程序的负担,在编写复杂业务程序的时候,还得时刻提醒自己不忘记添加这两个方法,所以,必须改正它。
2)通过JDK的反射机制重新实现这个逻辑代码如下:
public class PerformaceHandler implements InvocationHandler { private Object target; public PerformaceHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { PerformanceMonitoring.begin(target.getClass().getName()+"."+method.getName()); Object obj = method.invoke(target, args);//通过反射调用方法 PerformanceMonitoring.end(); return obj; } }
这是个代理实例调用处理程序的一个典型使用方式,通过指定一个目标对象,然后通过代理实例调用方法时,将其指派到它的调用处理程序的 invoke
方法。invoke方法在通过反射调用方法的前后添加了如上的监测代码,现来看看目标对象
public class JdkReflectService extends TestCase implements IJdkReflectService{ private static final JdkReflectService instance = new JdkReflectService(); public static JdkReflectService getInstance(){ return instance; } public void analogRemoveInfo() throws InterruptedException{ // PerformanceMonitoring.begin("analogRemoveInfo");//性能监测 Thread.currentThread().sleep(200);//模拟该方法执行了200毫秒 // PerformanceMonitoring.end();//结束性能监测 } public void testAnalogRemoveInfo() throws InterruptedException{ IJdkReflectService target = JdkReflectService.getInstance(); PerformaceHandler handler = new PerformaceHandler(target); IJdkReflectService proxy = (IJdkReflectService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass() .getInterfaces(), handler); proxy.analogRemoveInfo(); } }
可以看到在analogRemoveInfo() 方法中注释掉了如上的性能监测代码,通过JUnit Test运行可得到如上的结果。我们说过,JDK的代理是针对接口的,也就是说,即便像我们这个service中只有一个方法,为了使用JDK动态代理,我们还不得不多写一个service的接口,这下悲剧了,我们不禁会想:何必把简单的事情搞复杂呢?!OK,CGLib派上用场了:
3)
public class CglibProxy extends TestCase implements MethodInterceptor{ private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clz){ enhancer.setSuperclass(clz);//设置需要创建子类的类 enhancer.setCallback(this); return enhancer.create();//返回创建的子类对象 } /** * 拦截所有目录类方法 * @param arg0 目录类实例 * @param arg1 目标类方法的反射对象 * @param arg2 方法参数 * @param arg3 代理实例 * */ @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { PerformanceMonitoring.begin(arg0.getClass().getName()+"."+arg1.getName()); Object result = arg3.invokeSuper(arg0, arg2); PerformanceMonitoring.end(); return result; } public void testCglibProxy() throws InterruptedException{ CglibProxy proxy = new CglibProxy(); CglibReflectService service = (CglibReflectService) proxy.getProxy(CglibReflectService.class); service.analogRemoveInfo(); } }
可以看到,通过实现MethodInterceptor接口的唯一方法intercept,在intercept这个方法里加入耗时监测,通过getProxy方法获取代理实例,再调用对应的方法即可,具体的代码可参见附件,也请各位多多指正,共同进步。