用Groovy源编程(MOP)动态拦截(AOP)方法(比如记录String的concat和toUpperCase方法的耗费时间)
2012-09-10 17:32
447 查看
实现AOP的方式有很多种,像Spring的AOP,它只能拦截Spring托管的bean;Groovy AST Transformations、ASM等在编译阶段通过修改字节码也可以做AOP;JAVA HOOK也可以做,但比较麻烦。
Groovy MOP提供了一种很简单的方法实现AOP。
下面通过例子试用一下:
如果想动态拦截某个方法,不想改源代码(或者不能改源码,比如String已经是final类了),而能跟踪函数的执行时间(before invoke时记录开始时间,after invoke时记录完成时间,从而跟踪函数执行时间),可以用MOP实现。下面展示了3种方法:
方法一:用MOP重写具体的方法:
这种方法需要明确指定参数(String arg -> ),适用于具体明确的方法
方法二:用MOP重写invokeMethod:
这种方法可以在MOP时动态指定多个方法,不必一一定义。但是要小心死循环,它会拦截该类的所有方法。
方法三:注入MetaClass:
先定义MetaCalss:
然后再注册:
这种跟方法二其实是一样的,但是稍微繁琐点。
ExpandoMetaClass和Category也可以,可以自行研究一下。
关于效率问题:
使用MOP是否会影响效率呢,我做了个小测试程序试一试
函数执行需要花1秒钟。
正常执行:
执行结果:
正常耗时:1021
正常耗时:1003
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
正常耗时:1002
用MOP拦截:
执行结果:
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遇到反射调用时就拦截不到了!
这个问题不知谁有办法解决~~~
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)动态拦截(AOP)方法(比如记录String的concat和toUpperCase
- 从头认识Spring-3.6 简单的AOP日志实现(注解版)-需要记录方法的运行时间
- SpringAOP动态拦截方法并重写
- AspectJ spring aop 记录某些类中方法执行时间实例
- Aop之使用Castle动态代理实现对方法的拦截
- 从头认识Spring-3.2 简单的AOP日志实现-需要记录方法的运行时间
- spring boot aop 记录方法执行时间
- 使用Spring3.0的AOP结合log4j实现接口方法执行时间记录
- AspectJ spring aop 记录某些类中方法执行时间实例
- 利用Spring AOP记录方法的执行时间
- Aop 记录 方法被调用的时间 也可以做他用
- Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP
- 微软企业库5.0-面向切面AOP(PolicyInjection)学习记录--自定义属性匹配来拦截方法
- 动态规划;漂亮打印问题;时间复杂度O(n方); 思考方法记录在内;
- Spring AOP实践--记录类方法执行时间
- 使用动态代理记录方法执行的时间
- 记录每个方法的执行时间 AOP
- 【转载】使用动态代理记录方法执行的时间
- JAVA动态代理和方法拦截(使用CGLib实现AOP、方法拦截、委托)
- JAVA动态代理和方法拦截(使用CGLib实现AOP、方法拦截、委托)