Wednesday, February 2, 2011

Using @Configurable in Spring Framework: inject dependency to any object

Honestly, I like Spring Framework: awesome dependency management combined with rich features and great community. Recently I encountered very nice feature - @Configurable. In short, it allows developer to inject dependency into any object (who defines this annotation) without explicit bean definition. It's pretty cool. Techniques behind - AspectJ with runtime weaving.

Let me show how it works by creating simple test project. Let's start with Maven's project descriptor (pom.xml):

 4.0.0

 spring-configurable
 spring-configurable
 0.0.1-SNAPSHOT
 jar

 spring-configurable
 http://maven.apache.org

 
  UTF-8
  3.0.5.RELEASE
 
 
 
  
   org.aspectj
   aspectjweaver
   1.6.5
  
  
  
   org.springframework
   spring-beans
   ${spring.framework.version}
  
  
  
   org.springframework
   spring-aspects
   ${spring.framework.version}
  
  
  
   org.springframework
   spring-core
   ${spring.framework.version}
  
  
  
   org.springframework
   spring-context
   ${spring.framework.version}
  
  
  
   org.springframework
   spring-jdbc
   ${spring.framework.version}
  
 

Next one, let me create application context (applicationContext.xml) and place it inside src/main/resources:


   
 <context:annotation-config />
 <context:spring-configured />
 <context:load-time-weaver />
 
 <context:component-scan base-package="com.test.configurable" />
 
 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" />

Two key declarations here are <context:spring-configured /> and <context:load-time-weaver />. According to documentation, <context:spring-configured /> "... signals the current application context to apply dependency injection to non-managed classes that are
instantiated outside of the Spring bean factory (typically classes annotated with the @Configurable annotation)..."
and <context:load-time-weaver /> turns on runtime weaving.

Respective Java code which uses power of @Configurable annotation is located into Starter.java, which itself is placed inside src/main/java:
package com.test.configurable;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.Assert;

public class Starter {
 
 @Configurable
 private static class Inner {
  @Autowired private DataSource dataSource;  
 }
 
 public static void main(String[] args) {
  final ApplicationContext context = new ClassPathXmlApplicationContext( "/applicationContext.xml" );
  
  final DataSource dataSource = context.getBean( DataSource.class );
  Assert.notNull( dataSource );
  
  Assert.notNull( new Inner().dataSource );
 } 

}
As we see, class Inner is not a bean, is nested into another class and is created simply by calling new. Nevertheless, dataSource bean is injected as expected. Last but not least, in order this example to work, application should be run using an agent (I am using Spring Framework 3.0.5): -javaagent:spring-instrument-3.0.5.RELEASE.jar

11 comments:

Guillaume REBESCHE said...

Great post! I may use @Configurable some days! :)

dhorby said...

Very useful post. Easy to follow too.

Jorge said...

You don't really need <context:spring-configured /> as the <context:load-time-weaver /> already enables it. According to the xsd documentation:

"This also activates the current application context for applying dependency injection to non-managed classes that are instantiated outside of the Spring bean factory (typically classes annotated with the @Configurable annotation). This will only happen if the AnnotationBeanConfigurerAspect is on the classpath (i.e. spring-aspects.jar), effectively activating "spring-configured" by default."

Andriy Redko said...

Thanks a lot for pointing this out, less code or configuration in order to achieve the same goals is always a better way to do things. Thanks.

Nikhil Arora said...
This comment has been removed by the author.
Nikhil Arora said...
This comment has been removed by the author.
Nikhil Arora said...

how to configure my eclipse 8.6 to use below mentioned agent.
I am not using any build tool ike maven.

javaagent:C:\\spring-instrument.jar

Andriy Redko said...

Hi Nikhil,

It's pretty easy. Just open your project's Run Configuration (in Eclipse), [Arguments] tab and add please javaagent:C:\\spring-instrument.jar to the VM arguments text box. And that's it :-)

Please let me know if you need more details on that.

Thank you.

Best Regards,
Andriy Redko

Nikhil Arora said...

Hi Andriy
Thanks for the info.but actually i am using web app so the process was kind of different.Any way I have figured it out but now getting lot of errors and exception.
Can you look at the errors.I cannot paste in the blog coz its too long.

Andriy Redko said...

Hi Nikhil,

Sure, drop me a message on drreta at gmail.com. Thanks you.

Best Regards,
Andriy Redko

Shams said...

Thanks it works like charm.