We had some challenges regarding testing our jee app, and decided to try and integrate Spring to help out.
Our Application consists of multiple ear deployments, with a common library on the jboss classpath. We are using jboss AS 4.2.3.
Before I continue, I need to make it clear that I had no experience with Spring beforehand, everything I know about spring is from this experience.
I came across this article by Bill Burke which gave details on how to setup and deploy the spring deployer. This article (and provided links) provided enough detail to get the deployer working quickly. I needed to additionally configure the SpringInjectionInterceptor for my MBeans and MDB in the ejb3-interceptor.xml.
My only gripe with this configuration was that the interceptor used a non-standard annotation @Spring whereby you had to specify a JNDI Name and the bean you would like to inject. I got hold of the source code for the deployer and made some modifications :
1. The JNDI Name which the deployer configures is linked to the SpringInjectionInterceptor via the archived name it is deployed to (for example, We deploy everything as ear's so I would have foo.ear which would be linked to the JNDI name which the spring deployer would assign. This was non-trivial, since you need to get a reference to the deployer for the Spring Deployer and interceptor to link the two.)
2. In the jboss-spring.xml, I had to configure "component-scan", so my mbean which was annotated with @Component, @Service or @Repository would be configured as a Spring Bean. My modified interceptor would then determine whether the service was a spring bean and perform a lookup within the context for it
The main benefit from all of this, is that I could use completely standard spring annotation's in my jee services (mbean, ejb, mdb) and I did not have to worry about what the JNDI name the deployer assigned to the context was. The @Spring annotation was not needed anymore. The only caveat is that jboss and spring share the @resource annotation which causes some headaches - so I stuck to @autowired and @qualifier
So now all our jee services were behaving like standard spring beans, I wanted to springify my quartz jobs. I moved to using Spring's org.springframework.scheduling.quartz.SchedulerFactoryBean to inject my scheduler. A great motive for this is that you can pass the spring context to the scheduler so you can have access to it within your jee component which looked up the scheduler.
With the spring context in hand, we wrote a job factory which used this context to treat our quartz jobs as spring beans - this worked perfectly since now we could use standard spring annotations within our quartz jobs! (we did try and use spring's org.springframework.scheduling.quartz.SpringBeanJobFactory but did not instaniate the quartz jobs as spring beans)
What about pojo's?
We were at a stage where we had our jee services and quartz jobs springified, but what about standard prototype pojo's? After all, we wanted a full spring configuration for our entire application, not just selected components.
we discovered the @Configurable annotation which provided us with a means to completely springify our jee application. Using this annotation, we also did not have to use the SpringInjectionInterceptor or our custom job factory as mentioned above - we just annotated these service and jobs with @Configurable and upon instantiation (of course, this applied to any pojo!) we had a fully configured/injected spring bean.
To use the @Configurable annotation, we had to make sure that Spring could intercept the instantiation of the pojo's to configure them. The spring documents mentioned using Load-Time Weaving to do this. All we had to do was to ensure the spring-agent was setup, and spring + aspectj took care of the rest (there is a tiny piece of configuration required in the jboss-spring.xml to enable this).
This initially worked perfectly when we tested this client side (with 1 context for our application). However, we ran into problems when we deployed this to jboss:
Since our application consisted of multiple ears (which results in multiple spring contexts), any class that was shared amongst the ears in our common framework (ie spring contexts) would not be configured as a spring bean. Only the classes that were local to the ear (ie spring context) were spring configured. We found this post which mention's a similar problem.
We changed over to compile time weaving using aspectj since we could not find a workaround for the issue we experienced with Load-Time Weaving (if there is, please feel free to suggest one).
We are using ant, so we replaced our javac task with iajc. This increased our compilation time, but it solved all of our problems.
We are now running Spring + JEE side by side seamlessly (You can literally port our application from a JEE environment to a Spring environment and hence have the best of both worlds). The benefits from a testing and flow point of view are huge.
Our final configuration is:
- Jboss AS 4.2.3 (Our application consisted of multiple ear's sharing a common framework)
- Spring 2.5.6 (using @Configurable with compile-time weaving)
- Spring Deployer (standard version will do, the interceptor is not configured).
I hope this helps anyone who has the same requirements and has limited knowledge of spring.
We have recently moved over to Spring 3.0.2 and there was no changes necessary for the deployer or any other component (except having to upgrade to the latest junit 4 version).
Here are a few of the many reasons why we upgraded :
1. Various bug and stability fixes
2. Spring EL used for bean configuration
3. @Async annotation can be used to make async method invocations
4. @Autowire optimizations