实现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代码示例,利用Groovy的MOP机制动态热切入。
java 动态脚本语言 精通 Groovy
Groovy语言。 里面详细介绍了一些很有意思的方法。
Groovy 调用 Java 类groovy 调用 Java class 十分方便,只需要在类前导入该 Java 类,在 Groovy 代码中就可以无缝使用该
Java调用Groovy,实时动态加载数据库groovy脚本,java读取mongoDB的groovy脚本,加载实时运行,热部署
Groovy和Grails配置方法 教你从0入手,一步一步深入安装和测试,包含环境和语言的下载地址
groovy入门 groovy入门 groovy入门 groovy入门groovy入门groovy入门
自己总结的metaClass和ExpandoMetaClass的基本使用方法,代码量虽不到但是我觉得很有用处。
[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 DSL 动态规则(rule)执行引擎, 流程引擎. 特色 风控系统, 规则引擎, 动态接口配置(低代码)Groovy DSL 动态规则(rule)执行引擎。DSL(特定领域语言): 开发 和 业务 共识的语言。方便业务表达需求, 方便开发...
Groovy是JVM的一个替代语言(替代是指可以用 Groovy 在Java平台上进行Java 编程),使用方式基本与使用 Java代码的方式相同,该语言特别适合与Spring的动态语言支持一起使用,设计时充分考虑了Java集成,这使 ...
对javax.script包进行讲解,实现支持java动态嵌入执行groovy代码片段
groovy 敏捷 开发 动态 语言 急速 web 应用 开发
赠送jar包:groovy-3.0.9.jar; 赠送原API文档:groovy-3.0.9-javadoc.jar; 赠送源代码:groovy-3.0.9-sources.jar;...人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
本书是有关Groovy的第一本正式出版物,作者KennethBarclay和JohnSavage介绍了Groovy开发的所有主要领域,并解释了这种创新性的编程语言给Java平台赋予的动态特性。阅读本书只要求具备Java编程的一般性知识。不管你...
【Groovy】编译时元编程 ( ASTTransformation#visit 方法中访问 Groovy 类、方法、字段、属性 | 完整代码示例及进行编译时处理的编译过程 ) https://hanshuliang.blog.csdn.net/article/details/122815793 博客源码
《Groovy in Action》是Groovy编程的综合指南,它向Java开发者介绍了Groovy提供的新的动态特性。为了呈现《Groovy in Action》,Manning再次从源头工作,与包括Groovy项目团队成员和经理在内的专家作者团队合作。其结果...