用Groovy源编程(MOP)动态拦截(AOP)方法(比如记录String的concat和toUpperCase方法的耗费时间)

用Groovy源编程(MOP)动态拦截(AOP)方法(比如记录String的concat和toUpperCase方法的耗费时间),第1张

概述实现AOP的方式有很多种,像Spring的AOP,它只能拦截Spring托管的bean;Groovy AST Transformations、ASM等在编译阶段通过修改字节码也可以做AOP;JAVA HOOK也可以做,但比较麻烦。 Groovy MOP提供了一种很简单的方法实现AOP。 下面通过例子试用一下: 如果想动态拦截某个方法,不想改源代码(或者不能改源码,比如String已经是final类

实现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    }}

然后再注册:

        def amc = new MyMetaClass(String)        amc.initialize()        InvokerHelper.MetaRegistry.setMetaClass(String,amc)

这种跟方法二其实是一样的,但是稍微繁琐点。

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
正常耗时:1003
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002

用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
aop后耗时:1039
MOP包裹的函数耗费时间:1003
aop后耗时:1004
MOP包裹的函数耗费时间:1002
aop后耗时:1002
MOP包裹的函数耗费时间:1002
aop后耗时:1002
MOP包裹的函数耗费时间:1002
aop后耗时:1002
MOP包裹的函数耗费时间:1002
aop后耗时:1002
MOP包裹的函数耗费时间:1002
aop后耗时:1002
MOP包裹的函数耗费时间:1002
aop后耗时:1002
MOP包裹的函数耗费时间:1002
aop后耗时:1002
MOP包裹的函数耗费时间:1002
aop后耗时:1002

可见除头两次调用时间略长点,以后的执行时间是一样的。

原生的方法的执行时间MOP前后是差不多的,甚至包裹后还略快了点(第一次原生是1021ms,MOP后包裹的原生函数是1014ms),整个AOP的调用头两次略高点,后来就正常了(第一次是1039ms,比原生的1021ms慢了一点)。

从这个测试来看,用Groovy MOP实现AOP对效率的影响很小。

发现一个问题:当MOP遇到反射调用时就拦截不到了!

        Method m = String.class.getDeclaredMethod("toupperCase")        println "反射拦截不到----" + m.invoke(str)        println "正常调用OK----" + str.toupperCase()

这个问题不知谁有办法解决~~~

总结

以上是内存溢出为你收集整理的用Groovy源编程(MOP)动态拦截(AOP)方法(比如记录String的concat和toUpperCase方法的耗费时间)全部内容,希望文章能够帮你解决用Groovy源编程(MOP)动态拦截(AOP)方法(比如记录String的concat和toUpperCase方法的耗费时间)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/1266703.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存