Saturday, June 11, 2011

Using JSR 330 annotations with Spring Framework

No doubts Spring Framework had (and still has) a great influence on JSR 330: Dependency Injection for Java. For a long time Spring Framework has a pretty rich set of Java annotations in order to push dependency injection to superior levels. But ... most of such annotations are Spring-specific (like @Autowired, @Component, etc.). So ... the question is: does Spring Framework support JSR 330: Dependency Injection for Java? And the answer is: yes, it does, starting from version 3.

So let me show by example how to use Spring Framework together with JSR 330: Dependency Injection for Java. First, we need to reference JSR 330 annotations, f.e. from atinjet project. As always, I'm using Apache Maven 2/3 so here is my POM file:

  4.0.0

  com.example
  cdi-spring
  0.0.1-SNAPSHOT
  jar

  
    UTF-8
 3.0.5.RELEASE
  

  
    
      javax.inject
      javax.inject
      1
    
        
    
      org.springframework
      spring-context
      ${spring.version}
    
  
  
  
    
      nexus.xwiki.org
      http://nexus.xwiki.org/nexus/content/repositories/externals/
    
  

Pretty simple, the only thing we need to do is to have JSR 330 annotations in classpath. That's it. Here is my simple Spring context XML (applicationContext.xml):



    
    
 

I just asked Spring to do all job for me. Let me declare two simple beans, OneBean and AnotherBean, and inject one bean into another. So here is OneBean.java:
package com.spring.cdi;

import javax.inject.Named;

@Named
public class OneBean {
 public void doWork() {
  System.out.println( "Work is done" );
 }
}
And here is AnotherBean.java:
package com.spring.cdi;

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class AnotherBean {
 @Inject private OneBean oneBean;
 
 public void doWork() {
  oneBean.doWork();
 }
}
As you can see, there are no any Spring specific imports. Let me declare some Starter class which will host main() function (Starter.java):
package com.spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.cdi.AnotherBean;

public class Starter {
 public static void main( String[] args ) {
  ApplicationContext context = new ClassPathXmlApplicationContext( "applicationContext.xml" );
  AnotherBean bean = context.getBean( AnotherBean.class );
  bean.doWork();
 }
}
It just loads Spring application context from classpath, asks it for AnotherBean class and call the doWork() method on it (which delegates call to injected OneBean bean). Here is my log when I am running Starter class (please notice that Spring detected JSR 330 annotations and properly handled them):
Jun 11, 2011 1:08:03 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2626d4f1: startup date [Sat Jun 11 13:08:03 EDT 2011]; root of context hierarchy
Jun 11, 2011 1:08:03 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [applicationContext.xml]
Jun 11, 2011 1:08:03 PM org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider registerDefaultFilters
INFO: JSR-330 'javax.inject.Named' annotation found and supported for component scanning
Jun 11, 2011 1:08:04 PM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
Jun 11, 2011 1:08:04 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4979935d: defining beans [anotherBean,oneBean,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor]; root of factory hierarchy
Work is done
So those beans could be easily used without modifications by any Java framework which supports JSR 330 (f.e., JBoss Weld). Cool stuff.