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!
