Last updated by 5 years ago

Page: Developer - Dynamic Method Injection, Version:0

Dynamic Method Injection

The core classes that make up the Grails dynamic method injection architecture are found in the org.codhaus.groovy.grails.commons.metaclass package. The org.codhaus.groovy.grails.commons.metaclass.ProxyMetaClass class extends the Groovy MetaClass to allow constructor, method and property interceptors to be set on the MetaClass which "intercept" calls.

A Groovy MetaClass is what Groovy uses to dispatch method, constructor and property calls to objects with. These could be real methods or dynamically injected methods. Grails' ProxyMetaClass allows you to intercept calls to methods that may be real or not and choose whether to invoke the target "real" method at runtime. ProxyMetaClass tends to be used with instances and the setMetaClass method of GroovyObject as opposed to being registered within the MetaClassRegistry:

GroovyObject go = ..// some object
		ProxyMetaClass pmc = ProxyMetaClass.getInstance(go.getClass());
		go.setMetaClass(pmc)

Once you have a ProxyMetaClass instance registered as the objects MetaClass you can then set an interceptor on the ProxyMetaClass to handle the interception. There are three interfaces for this:

Fortunately, Grails provides an abstract implementation of these that makes life easier called AbstractDynamicMethodsInterceptor. This abstract class implements the DynamicMethods interface that allows you to add dynamic methods, contructors and properties as objects. For example the following code will register an anonymous AbstractDynamicMethodsInterceptor as the interceptor:

DynamicMethods dm = new AbstractDynamicMethodsInterceptor(){};
pmc.setInterceptor(dm);

You can then add dynamic methods, constructors and properties to the interceptor which implement either the DynamicMethodInvocation, DynamicConstructor or DynamicProperty interfaces. Fortunately there are abstract implementations of these all:

dm.addDynamicMethodInvocation(  new AbstractDynamicMethodInvocation("hello") {
           public Object invoke(Object target, Object[] arguments) {
                 System.out.println("world!");
           }
    });

Once the above has been added to my previously defined "go" object invoking the method from groovy will print "world!" to standard out:

go.hello() // prints "world!"

This mechanism is uses in several places including for controller and tag lib in the classes:

To make this even easier Grails provides a class called GroovyDynamicMethodsInterceptor which takes a GroovyObject in the constructor, registers a ProxyMetaClass on it and sets itself as the interceptor allowing you to shorten the above code example to:
GroovyObject go = //.. created from somewhere
    GroovyDynamicMethodsInterceptor i = new GroovyDynamicMethodsInterceptor(go);
    i.addDynamicMethodInvocation(  new AbstractDynamicMethodInvocation("hello") {
           public Object invoke(Object target, Object[] arguments) {
                 System.out.println("world!");
           }
    });

The proxy/interception mechanism is best used on instances of GroovyObject, but Grails provides another MetaClass called DelegatingMetaClass which takes an instance of the DynamicMethods interface in its constructor and delegates to it hence not requiring any interceptors to be set on it. Essentially it attempts to call a dynamic method first and if it fails falls back to trying to invoke a real method. This MetaClass is used by domain classes in conjunction with the DomainClassMethods class which is an instance of DynamicMethods.