實現AOP的方式有不少種,像Spring的AOP,它只能攔截Spring託管的bean;Groovy AST Transformations、ASM等在編譯階段經過修改字節碼也能夠作AOP;JAVA HOOK也能夠作,但比較麻煩。函數
Groovy MOP提供了一種很簡單的方法實現AOP。測試
下面經過例子試用一下:this
若是想動態攔截某個方法,不想改源代碼(或者不能改源碼,好比String已是final類了),而能跟蹤函數的執行時間(before invoke時記錄開始時間,after invoke時記錄完成時間,從而跟蹤函數執行時間),能夠用MOP實現。下面展現了3種方法:spa
方法一:用MOP重寫具體的方法:code
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 -> ),適用於具體明確的方法orm
方法二:用MOP重寫invokeMethod:get
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:it
先定義MetaCalss:io
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對效率的影響很小。