Groovy中的拦截方法

在Groovy中可以很轻松地实现AOP。这里有两种方法可以实现AOP:

  • 使用GroovyInterceptable拦截方法
  • 使用MetaClass拦截方法

使用GroovyInterceptable拦截方法

如果一个Groovy对象实现了GroovyInterceptable接口,那么当调用该对象上的任何一个方法时——不管是否存在该方法,被调用的都会是invokeMethod()。也就是,GroovyInterceptable上的invokeMethod方法总是会被调用的。

class Car implements GroovyInterceptable{

    def check(){
        System.out.println("check called...")
    }

    def start(){
        System.out.println("start called...")
    }

    def drive(){
        System.out.println("drive called...")
    }

    def invokeMethod(String name,  Object args) {
        System.out.print("call to $name intercepted... ")

        if(name != 'check'){
            System.out.print("running filter...")
            Car.metaClass.getMetaMethod('check').invoke(this,args)
        }

        def validMethod = Car.metaClass.getMetaMethod(name,args)
        if(validMethod != null){
            validMethod.invoke(this,args)
        }else{
            Car.metaClass.invokeMethod(this,name,args)
        }
    }
}

car = new Car()
car.start()
car.drive()
car.check()

try{
    car.stop()
}catch (Exception e){
    println e
}

使用MetaClass拦截方法

如果我们用的是第三方的源码或者用的是Java类,那么我们就不能用实现GroovyInterceptable接口的方法进行拦截了。在这种情况下,我们可以在MetaClass上实现invokeMethod()方法,并以此来拦截。

class Auto {

    def check() {
        System.out.println("check called...")
    }

    def start() {
        System.out.println("start called...")
    }

    def drive() {
        System.out.println("drive called...")
    }
}

Auto.metaClass.invokeMethod = { String name, Object args ->
    System.out.print("Call to $name intercepted...")

    if (name != 'check') {
        System.out.print("running filter...")
        Auto.metaClass.getMetaMethod('check').invoke(delegate, null)
    }

    def validMethod = Auto.metaClass.getMetaMethod(name, args)
    if (validMethod != null) {
        validMethod.invoke(delegate, args)
    } else {
        Auto.metaClass.invokeMissingMethod(delegate, name, args)
    }
}

auto = new Auto()

auto.start()
auto.drive()
auto.check()

try {
    auto.stop()
} catch (Exception e) {
    println e
}

上面的代码中,以闭包的形式实现了invokeMethod()。同时使用了delegate,delegate指向的是要拦截其方法的目标对象。

如果我们要对POJO进行拦截的话,原理是一样的,看一段对Integer进行拦截的代码。

Integer.metaClass.invokeMethod = {String name,Object args ->
    System.out.println("Call to $name interccepted on $delegate...")

    def validMethod = Integer.metaClass.getMetaMethod(name,args)
    if(validMethod == null){
        Integer.metaClass.invokeMissingMethod(delegate,name,args)
    }else{
        System.out.println("running pre-filter... ")
        result = validMethod.invoke(delegate,args)
        System.out.println("running post-filter... ")
        result
    }
}

println 5.floatValue()
println 5.intValue()

try{
    println 5.isEmpty()
}catch (Exception e){
    println e
}

ps:ExpandoMethodClass是MetaClass接口的一个实现,也是Groovy种负责实现多态行为的关键类之一。通过向该类添加方法可以实现向类中注入行为,甚至可以使用该类特化单个对象。默认情况下,Groovy目前并没有使用ExpandoMethodClass。当我们想metaClass中添加一个方法时,默认的metaClass会被用一个ExpandoMetaClass实例替换掉。(注意obj1,obj2的结果…)

import groovy.transform.Immutable

def printMetaClassInfo(instance){
    print "MetaClass of ${instance} is ${instance.metaClass.class.simpleName}"
    println " with delegate ${instance.metaClass.delegate.class.simpleName}"
}

printMetaClassInfo(2)
println "MetaClass of Integer is ${Integer.metaClass.class.simpleName}"
println "Adding a method to Integer metaClass"
Integer.metaClass.someNewMethod = {-> /* */}
printMetaClassInfo(2)
println "MetaClass of Integer is ${Integer.metaClass.class.simpleName}"

@Immutable
class MyClass{
    String name
}
obj1 = new MyClass("obj1")

printMetaClassInfo(obj1)
println "Adding a method to MyClass metaClass"
MyClass.metaClass.someNewMethod = {-> /* */}
printMetaClassInfo(obj1)

println "obj2 created later"
obj2 = new MyClass("obj2")
printMetaClassInfo(obj2)