`
xiefeifeihu
  • 浏览: 99100 次
  • 性别: 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的代码示例,例如如何创建和使用ExpandoMetaClass,如何添加动态方法和属性,或者如何实现拦截器。通过分析和学习这些示例,你可以更好地理解和掌握Groovy MOP的...

    groovy动态方法

    总的来说,`groovy动态方法`和`MetaClass`、`ExpandoMetaClass`以及MOP是Groovy强大特性的体现,它们为开发者提供了在运行时改变类行为的能力,使得Groovy代码更加灵活多变。掌握这些概念和技巧,将有助于你编写出更...

    java 动态脚本语言 精通 Groovy

    Groovy是一种基于Java平台的动态脚本语言,它在Java开发者中越来越受欢迎,因为它提供了简洁、灵活的语法,以及强大的动态编程能力。Groovy与Java兼容性极佳,可以直接调用Java类库,使得它在Java生态系统中具有广泛...

    groovy和Java相互调用1

    标题中的“Groovy和Java相互调用1”指的是在编程时如何在Groovy语言环境中调用Java类,以及反之,如何在Java程序中调用Groovy类。这是一种跨语言交互的方式,特别是在混合使用Groovy和Java的项目中非常常见。 ...

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

    Java作为一种广泛使用的静态类型编程语言,有时候可能无法满足所有需求,特别是在处理动态代码执行和热部署时。此时,Groovy这种基于JVM的动态语言就发挥了重要作用。本文将详细讲解如何在Java应用程序中调用Groovy...

    Groovy脚本:Java平台的动态编程利器

    本文将详细介绍Groovy脚本的基本概念、特点、用途以及如何在Java平台中使用Groovy进行动态编程。 Groovy脚本语言以其简洁的语法、动态特性和与Java的紧密集成,在Java平台中发挥着重要作用。从动态脚本执行到构建...

    Groovy in Action源代码

    8. **集成测试**:Groovy提供了Spock框架进行行为驱动开发(BDD)和单元测试,源代码中可能包含使用Spock进行测试的示例,帮助理解测试驱动开发的方法。 9. **领域特定语言(DSL)**:Groovy因其简洁的语法和强大的...

    groovy 最新 学习 动态

    Groovy是一种基于Java平台的动态编程语言,设计用于提高开发者的生产力和代码的简洁性。在Java生态系统中,Groovy以其灵活性和与Java的良好互操作性而受到广泛关注。本篇文章将深入探讨Groovy的最新学习动态,特别是...

    Sping AOP编程

    切点表达式是用Groovy语法编写的,用于精确匹配需要拦截的方法。例如,`execution(* com.example.service.*.*(..))`表示拦截`com.example.service`包下的所有类的所有方法。 **4. 环绕通知(Around Advice)** ...

    JVM 动态执行Groovy脚本的方法

    本文将详细讲解如何使用JVM动态执行Groovy脚本的方法,主要包括利用JShell执行代码、调试模式下动态执行代码以及利用javax.script包执行Groovy脚本。以下是对各知识点的详细说明。 1. 利用JShell执行代码 Java 9 ...

    Groovy和Grails配置方法

    ### Groovy和Grails配置方法 #### 一、Groovy与Grails简介 Groovy是一种强大的面向对象编程语言,它运行在Java平台上,并且能够直接与Java代码进行交互。Groovy支持函数式编程特性,拥有丰富的语法糖以及简洁的...

    Groovy Script 入门

    Groovy不仅支持传统的面向对象编程特性,还融合了函数式编程和元编程的特性,这使得Groovy脚本成为处理日常开发任务的强大工具。 #### 二、Groovy脚本的特点 1. **与Java的高度兼容性**:Groovy可以无缝地与Java...

    apache-groovy-sdk-3.0.6.zip

    8. **动态和静态类型**:Groovy允许开发者在同一个项目中同时使用动态和静态类型的代码,这在保证性能的同时,也保持了代码的灵活性。 9. **元编程**:Groovy的元编程能力强大,允许在运行时修改类和对象的行为,这...

    groovy-api-chm

    5. **元编程**:Groovy允许在运行时修改类和对象的行为,这是通过MetaObjectProtocol (MOP) 实现的。例如,可以动态添加方法、属性,或者改变现有方法的行为。 6. **GDK(Groovy Development Kit)**:GDK是Groovy...

    Java中使用Groovy的三种方式

    与Java的Spring Boot框架结合使用时,Groovy可以作为Spring Boot的源代码语言,提供更加简洁的配置和更快的开发速度。Groovy的动态性使得编写Spring配置和Bean定义更加直观,而Spring Boot的"约定优于配置"理念与...

Global site tag (gtag.js) - Google Analytics