Method Interception And Synthesis With Groovy

Adding code like filters, validations or logging to some or all methods of a class is a neat feature. I’d like to show how to do this in Groovy.

Method interception is the way of catching calls to existing methods and add some logic (but don’t forget to execute the desired call in the interception method). Method synthesis allows to dynamically add methods at runtime.

Thanks to Groovy’s Meta Object Protocol or MOP we can intercept method calls and add methods at runtime. This is achieved by the interface GroovyInterceptable implemented by the class GroovyObject.

Define a class with one method:

class C {
    def bla(i) {
        // Just return parameter
        i
    }
    // Method blub is not defined
}

If a call to a non-existing method should be done, use methodMissing on the metaclass:

/**
 * Called when a method was not found.
 */
C.metaClass.methodMissing = { String name, args ->
    println "methodMissing: " + name
    // Do something depending on name and args...
}

For method interception implement invokeMethod on the metaclass:

/**
 * Intercept any method call on the class.
 */
C.metaClass.invokeMethod = { String name, args ->
    // Do something before method call
    println "before calling method ${name}"
    // Lookup existing method...
    def m = delegate.metaClass.getMetaMethod(name, *args)
    // ...and call it. If we cannot find it, delegate call to methodMissing
    def result = (m ? m.invoke(delegate, *args) : delegate.metaClass.invokeMissingMethod(delegate, name, args))
    // Do something after method call
    println "after calling method ${name}"
    // Return result
    result
}

See it in action, call bla(i) and non-existing method blub():

c = new C()
println c.bla(5)
println c.bla(10)
println c.blub()

Result:

before calling method bla
5
after calling method bla
before calling method bla
10
after calling method bla
methodMissing: blub
null

This also works on the class itself defining a static invokeMethod:

C.metaClass.static.invokeMethod = { String name, args ->
}

One more time:

println C.bla(5)
println c.blub()

Result:

before calling method bla
5
after calling method bla
methodMissing: blub
null

Methods defined at runtime using C.metaClass are intercepted, too:

A.metaClass.static.foo = { -> "foo!" }
println A.foo()

Gives:

invokeMethod: foo
foo!

Have fun!

This entry was posted in Groovy, Software Development. Bookmark the permalink.

Leave a Reply