用Groovy源编程(MOP)动态拦截(AOP)方法(比如记录String的co
实现AOP的方式有很多种,像Spring的AOP,它只能拦截Spring托管的bean;Groovy AST Transformations、ASM等在编译阶段通过修改字节码也可以做AOP;JAVA HOOK也可以做,但比较麻烦。 Groovy MOP提供了一种很简单的方法实现AOP。 ? 下面通过例子试用一下: 如果想动态拦截某个方法,不想改源代码(或者不能改源码,比如String已经是final类了),而能跟踪函数的执行时间(before invoke时记录开始时间,after invoke时记录完成时间,从而跟踪函数执行时间),可以用MOP实现。下面展示了3种方法: ? 方法一:用MOP重写具体的方法:
def recordDuration_concat() {
// 保存原有方法 def savedMethod = String.metaClass.getMetaMethod('concat',[String] as Class[])
// 开始改变原有方法 String.metaClass.concat = {String arg ->
long s = System.currentTimeMillis(); def result = savedMethod.invoke(
delegate,arg)
long e = System.currentTimeMillis();
long duration = e - s; println("
MOP耗费时间:" + duration);
return result; } }
这种方法需要明确指定参数(String arg -> ),适用于具体明确的方法 ? 方法二:用MOP重写invokeMethod:
def recordDuration_invokeMethod() { String.metaClass.invokeMethod = {String mName,mArgs -> def m = String.metaClass.getMetaMethod(mName,mArgs)
if (mName != "
concat" && mName != "
toUpperCase")
return m.invoke(
delegate,mArgs)
long s = System.currentTimeMillis(); def result = m.invoke(
delegate,mArgs)
long e = System.currentTimeMillis();
long duration = e - s; println("
MOP耗费时间:" + duration);
return result; } }
这种方法可以在MOP时动态指定多个方法,不必一一定义。但是要小心死循环,它会拦截该类的所有方法。 ? 方法三:注入MetaClass: 先定义MetaCalss:
public
class MyMetaClass
extends DelegatingMetaClass { MyMetaClass(Class thisClass) {
super(thisClass) } Object invokeMethod(Object
object,String methodName,Object[] arguments) {
if (methodName != "
concat" && methodName != "
toUpperCase")
return
super.invokeMethod(
object,methodName,arguments)
long s = System.currentTimeMillis(); def result =
super.invokeMethod(
object,arguments)
long e = System.currentTimeMillis();
long duration = e - s; println("
MOP耗费时间:${duration}");
return result } }
然后再注册: 这种跟方法二其实是一样的,但是稍微繁琐点。 ? ExpandoMetaClass和Category也可以,可以自行研究一下。 ? 关于效率问题: 使用MOP是否会影响效率呢,我做了个小测试程序试一试
public
class TC {
public
void call() { sleep(1000) } }
函数执行需要花1秒钟。 正常执行:
def testNormal() { 1.upto(10000) {
long s = System.currentTimeMillis()
new TC().call()
long e = System.currentTimeMillis() println "
正常耗时:${e - s}" } }
执行结果: 正常耗时:1021 ? 用MOP拦截:
def recordDuration_call() { TC.metaClass.invokeMethod = {String mName,mArgs -> def m = TC.metaClass.getMetaMethod(mName,mArgs)
long e = System.currentTimeMillis();
long duration = e - s; println("
MOP包裹的函数耗费时间:" + duration);
return result; } } def testAop() { recordDuration_call() 1.upto(10000) {
long s = System.currentTimeMillis()
new TC().call()
long e = System.currentTimeMillis() println "
aop后耗时:${e - s}" } }
执行结果: MOP包裹的函数耗费时间:1014 ? 可见除头两次调用时间略长点,以后的执行时间是一样的。 原生的方法的执行时间MOP前后是差不多的,甚至包裹后还略快了点(第一次原生是1021ms,MOP后包裹的原生函数是1014ms),整个AOP的调用头两次略高点,后来就正常了(第一次是1039ms,比原生的1021ms慢了一点)。 从这个测试来看,用Groovy MOP实现AOP对效率的影响很小。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |