`
xiefeifeihu
  • 浏览: 97298 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

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

阅读更多

 

实现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, methodName, 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 s = System.currentTimeMillis();
            def result = m.invoke(delegate, 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()

 

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

1
0
分享到:
评论

相关推荐

    Groovy MOP

    Groovy MOP代码示例,利用Groovy的MOP机制动态热切入。

    java 动态脚本语言 精通 Groovy

    java 动态脚本语言 精通 Groovy

    Groovy高效编程

    Groovy语言。 里面详细介绍了一些很有意思的方法。

    groovy和Java相互调用1

    Groovy 调用 Java 类groovy 调用 Java class 十分方便,只需要在类前导入该 Java 类,在 Groovy 代码中就可以无缝使用该

    Java调用Groovy,实时动态加载数据库groovy脚本

    Java调用Groovy,实时动态加载数据库groovy脚本,java读取mongoDB的groovy脚本,加载实时运行,热部署

    Groovy和Grails配置方法

    Groovy和Grails配置方法 教你从0入手,一步一步深入安装和测试,包含环境和语言的下载地址

    groovy入门编程

    groovy入门 groovy入门 groovy入门 groovy入门groovy入门groovy入门

    groovy动态方法

    自己总结的metaClass和ExpandoMetaClass的基本使用方法,代码量虽不到但是我觉得很有用处。

    [Groovy] Groovy 2 编程 (英文版)

    [Pragmatic Bookshelf] Groovy 2 编程 (英文版) [Pragmatic Bookshelf] Programming Groovy 2 Dynamic Productivity for the Java Developer (E-Book) ☆ 出版信息:☆ [作者信息] Venkat Subramaniam [出版机构...

    Groovy in Action源代码

    Groovy in Action的源代码。Groovy in Action的源代码。

    Groovy DSL 动态规则(rule)执行引擎流程引擎特色风控系统规则引擎动态接口配置(低代码)

    自创Groovy DSL 动态规则(rule)执行引擎, 流程引擎. 特色 风控系统, 规则引擎, 动态接口配置(低代码)Groovy DSL 动态规则(rule)执行引擎。DSL(特定领域语言): 开发 和 业务 共识的语言。方便业务表达需求, 方便开发...

    Groovy v2.4.13官方版

     Groovy是JVM的一个替代语言(替代是指可以用 Groovy 在Java平台上进行Java 编程),使用方式基本与使用 Java代码的方式相同,该语言特别适合与Spring的动态语言支持一起使用,设计时充分考虑了Java集成,这使 ...

    JVM 动态执行Groovy脚本的方法

    对javax.script包进行讲解,实现支持java动态嵌入执行groovy代码片段

    groovy 最新 学习 动态

    groovy 敏捷 开发 动态 语言 急速 web 应用 开发

    groovy-3.0.9-API文档-中文版.zip

    赠送jar包:groovy-3.0.9.jar; 赠送原API文档:groovy-3.0.9-javadoc.jar; 赠送源代码:groovy-3.0.9-sources.jar;...人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。

    Groovy入门经典.pdf

     本书是有关Groovy的第一本正式出版物,作者KennethBarclay和JohnSavage介绍了Groovy开发的所有主要领域,并解释了这种创新性的编程语言给Java平台赋予的动态特性。阅读本书只要求具备Java编程的一般性知识。不管你...

    ASTTransformation#visit 方法中访问 Groovy 类、方法、字段、属性

    【Groovy】编译时元编程 ( ASTTransformation#visit 方法中访问 Groovy 类、方法、字段、属性 | 完整代码示例及进行编译时处理的编译过程 ) https://hanshuliang.blog.csdn.net/article/details/122815793 博客源码

    Groovy in Action 中文版

    《Groovy in Action》是Groovy编程的综合指南,它向Java开发者介绍了Groovy提供的新的动态特性。为了呈现《Groovy in Action》,Manning再次从源头工作,与包括Groovy项目团队成员和经理在内的专家作者团队合作。其结果...

Global site tag (gtag.js) - Google Analytics