Using Annotations To Inject – Metadata Driven AOP
New releases of compilers are getting more and more cautious on taking off the pain of too much verbosity and making the code look much more cleaner, understandable and easily modifiable. A major step that has been taken in Java side is introduction of annotations. Now that they have introduced annotations, plugging in an annotation processor still seems like an unsolved job requiring changes of the level of JVM startup arguments.
Issues with existing APT (Annotation Processing Tool) :
- The existing API involves changes to the JVM arguments in case some change is required.
- Changes at runtime will not be possible as once a annotation processor is started, it will add the processing instructions. You cannot change this behavior in an already running application.
I was reading through one article that involved AOP and looked at one of the sample snippets that described about instrumenting Transactional attributes, so called boiler plate code for handling transactions into the services. The text clearly mentioned that it is possible or rather very easy to add metadata based advising into the code. After reading that line, I actually thought that it can be a possibility that the metadata processor or so called Pointcut can be built based on annotation processing and the Advice implementation can contain the code that you need to instrument or simply hide from your code because you think it is boiler plate.
By implementing this mechanism, you just change your application context file and thus avoid the headache of changing the startup arguments.
Also you can configure your context prior to starting in case you don't want your annotations to become active.
The code part :
Usually its the most tricky part but in our case, this seems the least complex. There is one class named DefaultAdvisorAutoProxyCreator which needs to be added to the usual applicationContext.xml file. You also need to create your own Annotation and define its settings. Last but not the least, you need a PointcutAdvisor which will tell spring about which classes to process and what should be the processing.
In the sample code below, I would be adding a SpookyAnnotation on top of a bean and will allow the SpookyingInterceptor to create a proxy of the bean with the injected code. The SpookyingInterceptor does nothing but adds the advice of a System.out.println statement in the methods of the bean which has this annotation present.
<bean:bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" /> <bean:bean class="test.beans.interceptor.SpookyingInterceptor" /></pre>
@Retention(value= RetentionPolicy.RUNTIME)
public @interface SpookyAnnotation {
boolean insertSpookiness() default true;
}
package test.beans.interceptor;
import ....
import test.annotations.SpookyAnnotation;
public class SpookyingInterceptor extends AbstractPointcutAdvisor{
public Pointcut getPointcut() {
return new Pointcut() {
public MethodMatcher getMethodMatcher() {
return new MethodMatcher() {
public boolean matches(Method arg0, Class<?> arg1, Object[] arg2) {
.....
}
public boolean matches(Method arg0, Class<?> arg1) {
.....
}
public boolean isRuntime() {
.....
}
};
}
public ClassFilter getClassFilter() {
return new ClassFilter() {
public boolean matches(Class<?> clazz) {
if(clazz.getAnnotation(SpookyAnnotation.class) == null) {
return false;
} else {
return clazz.getAnnotation(SpookyAnnotation.class)
.insertSpookiness();
}
}
};
}
};
}
public Advice getAdvice() {
return new MethodBeforeAdvice() {
public void before(Method method, Object[] methodArgs, Object objOfClazz)
throws Throwable {
System.out.println("The method " + method.getName()
+ " has been spooked!!");
}
};
}
}
package test.beans;
import test.annotations.SpookyAnnotation;
@SpookyAnnotation(insertSpookiness = false)
public class BeanWithNonSpookyCode {
public void callMe() {
System.out.println("non spooky one has been called");
}
}
package test.beans;
public class AnotherBeanWithNonSpookyCode {
public void callMe() {
System.out.println("non spooky one has been called");
}
}
package test.beans;
import test.annotations.SpookyAnnotation;
@SpookyAnnotation
public class BeanWithSpookyCode {
public void callMe() {
System.out.println("spooky one has been called");
}
}
<bean:bean id="beanWithNonSpooky" class="test.beans.BeanWithNonSpookyCode" /> <bean:bean id="anotherBeanWithNonSpooky" class="test.beans.AnotherBeanWithNonSpookyCode" /> <bean:bean id="beanWithSpooky" class="test.beans.BeanWithSpookyCode" />
MethodBeforeAdvice is a standard class which instruments the code in before method before the method call.
Conclusion :
- This style of coding is very highly plug and play because removing the PointcutAdvisor bean from applicationContext.xml will stop annotation processing for this particular annotation and you just need to add annotation to the class which you need to apply the Advice to. Rest of the code would remain untouched.
- You might feel some startup delay with this code because initializing this structure and instrumenting code surely needs some time, but execution time remains unaffected.
- You need CGLIB in your class-path for using AOP. Rest is normal spring dependencies that are added to the project.
- Java version should at-least be 1.5 because annotations are not supported below that.
Filed under: AOP, Architecture, Blog, Java, Spring

[...] This post was mentioned on Twitter by Rohit, Xebia India. Xebia India said: A new blog on Xebee, Using Annotations To Inject – Metadata Driven AOP – http://bit.ly/dZKl8t [...]