Monday, June 30, 2008

Using custom spring annotations in the Web and App tier

I had blogged earlier about why I thought Spring 2.5 annotations were really cool and well implemented. I had mentioned in that article how one could use one's own custom annotations through Spring 2.5 to inject dependencies into web application classes as well as the EJBs in the application tier. I went ahead and tried this out for the web and app tier to see how challenging this really was. It required the web classes that had my custom annotations to be instantiated through Spring. The Struts2 framework that's really getting a lot of momentum these days (check out Matt Raible's stats on this here) integrates well with Spring and though the documentation is not thorough, the link here gives a fairly good idea of how one goes about Spring enabling struts2. The gist here is that the object factory for all struts actions is the StrutsSpringObjectFactory. The configuration of my web.xml really needed only this listener:

  <listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

I also had an applicationContext.xml (under WEB-INF) that scanned for my own custom annotations. So that allowed my Action classes to use my custom annotations. With Struts2's native annotations, you don't need to worry about the struts-config.xml for result-jsp mappings either. Its all there in your action class. I even had advice around the action to capture timing stats thus implementing "seperation of concerns" to the hilt!! Well that took care of the web tier.

My app tier was EJB 3.0 based and I found that Spring had an interceptor that helped my cause. So my session facade had to be annotated with my custom Spring interceptor as shown here:

@Stateless(name = "HelloWorldService")
@Interceptors(MySpringBeanAutowiringInterceptor.class)
public class HelloWorldService implements HelloWorld, Serializable

The custom Spring interceptor essentially helps me to use a custom annotation (my own type for autowiring) in the EJB. The code for the interceptor looks like:

public class MySpringBeanAutowiringInterceptor extends SpringBeanAutowiringInterceptor
{
@Override
protected void configureBeanPostProcessor(AutowiredAnnotationBeanPostProcessor processor, Object target) {
processor.setAutowiredAnnotationType(CustomAutowiring.class);
}
}

Now here came the tricky part - I had to create the beanRefContext.xml for Spring and had to have it load up another context file that had information to setup Spring to search for my custom annotations as so -

<bean class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg type="java.lang.String" value="ejbApplicationContext.xml"/>
</bean>

In the ejbApplicationContext.xml, I setup the component-scan tag to search for my annotations. This worked though I was initially at a loss as to why I need to specify two context files to get dependencies to be injected by Spring. I found the reason for this on the Spring forums (by Juergen Hoeller) -

Essentially, your "beanRefContext.xml" file will typically define a ClassPathXmlApplicationContext which in turns loads your actual beans. The "beanRefContext.xml" mechanism allows multiple such shared ApplicationContexts to co-exist, differentiated by bean factory locators. In the default EJB3 case, simply define one such ApplicationContext which will be picked up automatically, regardless of name.

You can also read more on this in the Spring docs here. The other thing also was that I had to make sure I had the correct set of libraries in my EAR. Those libraries were:

- spring.jar
- asm-2.2.3.jar
- asm-commons-2.2.3.jar
- aspectjrt.jar
- aspectjweaver.jar
- common-annotations.jar
- commons-logging.jar
- log4j-1.2.14.jar

Overall, I found that the web side Struts2 integration with Spring seemed much more cleaner that the EJB one. At this point I would still recommend this approach, but make sure you have a little bit of time on your hands to get this working exactly the way you want it. If you are interested, I can further share any other information - just call out.

I will continue to tweak and research on the EJB implementation - but overall, this approach still makes for an interesting application design.

1 comment:

Sombra said...

HI. I'm trying to do the same thing but I can't make it work. Can you put your example code available?
Thanks in advance