实现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()
这个问题不知谁有办法解决~~~
分享到:
相关推荐
在提供的压缩包文件中,很可能是包含了一些Groovy MOP的代码示例,例如如何创建和使用ExpandoMetaClass,如何添加动态方法和属性,或者如何实现拦截器。通过分析和学习这些示例,你可以更好地理解和掌握Groovy MOP的...
总的来说,`groovy动态方法`和`MetaClass`、`ExpandoMetaClass`以及MOP是Groovy强大特性的体现,它们为开发者提供了在运行时改变类行为的能力,使得Groovy代码更加灵活多变。掌握这些概念和技巧,将有助于你编写出更...
Groovy是一种基于Java平台的动态脚本语言,它在Java开发者中越来越受欢迎,因为它提供了简洁、灵活的语法,以及强大的动态编程能力。Groovy与Java兼容性极佳,可以直接调用Java类库,使得它在Java生态系统中具有广泛...
标题中的“Groovy和Java相互调用1”指的是在编程时如何在Groovy语言环境中调用Java类,以及反之,如何在Java程序中调用Groovy类。这是一种跨语言交互的方式,特别是在混合使用Groovy和Java的项目中非常常见。 ...
Java作为一种广泛使用的静态类型编程语言,有时候可能无法满足所有需求,特别是在处理动态代码执行和热部署时。此时,Groovy这种基于JVM的动态语言就发挥了重要作用。本文将详细讲解如何在Java应用程序中调用Groovy...
本文将详细介绍Groovy脚本的基本概念、特点、用途以及如何在Java平台中使用Groovy进行动态编程。 Groovy脚本语言以其简洁的语法、动态特性和与Java的紧密集成,在Java平台中发挥着重要作用。从动态脚本执行到构建...
8. **集成测试**:Groovy提供了Spock框架进行行为驱动开发(BDD)和单元测试,源代码中可能包含使用Spock进行测试的示例,帮助理解测试驱动开发的方法。 9. **领域特定语言(DSL)**:Groovy因其简洁的语法和强大的...
Groovy是一种基于Java平台的动态编程语言,设计用于提高开发者的生产力和代码的简洁性。在Java生态系统中,Groovy以其灵活性和与Java的良好互操作性而受到广泛关注。本篇文章将深入探讨Groovy的最新学习动态,特别是...
切点表达式是用Groovy语法编写的,用于精确匹配需要拦截的方法。例如,`execution(* com.example.service.*.*(..))`表示拦截`com.example.service`包下的所有类的所有方法。 **4. 环绕通知(Around Advice)** ...
本文将详细讲解如何使用JVM动态执行Groovy脚本的方法,主要包括利用JShell执行代码、调试模式下动态执行代码以及利用javax.script包执行Groovy脚本。以下是对各知识点的详细说明。 1. 利用JShell执行代码 Java 9 ...
### Groovy和Grails配置方法 #### 一、Groovy与Grails简介 Groovy是一种强大的面向对象编程语言,它运行在Java平台上,并且能够直接与Java代码进行交互。Groovy支持函数式编程特性,拥有丰富的语法糖以及简洁的...
Groovy不仅支持传统的面向对象编程特性,还融合了函数式编程和元编程的特性,这使得Groovy脚本成为处理日常开发任务的强大工具。 #### 二、Groovy脚本的特点 1. **与Java的高度兼容性**:Groovy可以无缝地与Java...
8. **动态和静态类型**:Groovy允许开发者在同一个项目中同时使用动态和静态类型的代码,这在保证性能的同时,也保持了代码的灵活性。 9. **元编程**:Groovy的元编程能力强大,允许在运行时修改类和对象的行为,这...
5. **元编程**:Groovy允许在运行时修改类和对象的行为,这是通过MetaObjectProtocol (MOP) 实现的。例如,可以动态添加方法、属性,或者改变现有方法的行为。 6. **GDK(Groovy Development Kit)**:GDK是Groovy...
与Java的Spring Boot框架结合使用时,Groovy可以作为Spring Boot的源代码语言,提供更加简洁的配置和更快的开发速度。Groovy的动态性使得编写Spring配置和Bean定义更加直观,而Spring Boot的"约定优于配置"理念与...