Combining standalone application with Spring

When you are developing a standalone application, that you want to use with the SpringFramework, somewhere in your code you will have to load the application context. Potentially this results in chicken-egg problem, especially when you want to use a component in some class that is not a bean. This may have different causes:

  • It is the main class, that is used to start up the application
  • The constructor uses arguments that cannot be resolved with autowireing

There are different approaches to solve this, the most elegant one I will demonstrate.Some brains storming produces the following approaches:

  • Use of a factory class that can be used to initialize the no-bean class
  • Using aspects to resolve the autowiring dependencies
  • Initiate autowiring manually

The factory approach

While this is a simple solution, it is not entirely elegant. It involves the creation of a factory class as a bean which has the components wired in, that are needed to create the non-bean class and some static create method. However the fact that the factory is a bean is not exploited. It has to be declared as a bean so autowiring can take place.

Using Aspects

This is perhaps the most elegant solution from the view point of the application code, as the autowiring works as if it were a bean. On the downside the Aspect needs to be declared.

If the constructor calls a init method, this can be intercepted. Unfortunately you can only intercept public methods with Spring AOP, as it is proxy based. Additionally to define the pointcut you should define a annotation for the class, so you can single out all the classes with an init method, which should be considered by this aspect.

Using AspectJ for this seams the better solution, as then the pointcut can be defined on the level of the constructor. The actual code that has to be executed looks similar to the one in the manual case.

Initialize autowiring manually

The first question that pops up is, why would anyone do such a thing? Why not wire the dependencies manually:

  private ComponentConsumer consumer; // <== Spring main class

  public Main(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    consumer = context.getBean(ComponentConsumer.class);
  }

One answer is flexibility. If you have only one bean to wire both approaches are about the same. However if you have more, you get an additional line of code for each additional bean. The other point is expendability during time: when you want to add a dependency to another bean tomorrow, you will have to define the bean and its initialisation.

But now let’s have a look at some code. First there are two beans one a Service and one a Component:

package ch.sahits.test.spring.components;

import org.springframework.stereotype.Component;

@Component
public class SimpleWriter {

  public void sayHello() {
    System.out.println("Hello Spring DI Component!");
  }
}
package ch.sahits.test.spring.components;

import org.springframework.stereotype.Service;

@Service
public class SimpleWriterService {

  public void sayHello() {
    System.out.println("Hello Spring DI service!");
  }
}

Next is another Component, which consumes from these two writers:

package ch.sahits.test.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ch.sahits.test.spring.components.SimpleWriter;
import ch.sahits.test.spring.components.SimpleWriterService;

@Component
public class ComponentConsumer {

  @Autowired
  private SimpleWriter component;
  @Autowired
  private SimpleWriterService service;

  public void consume() {
    component.sayHello();
    service.sayHello();
  }
}

Now the really interesting part is the class containing the main method.

package ch.sahits.test.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
  @Autowired
  private ComponentConsumer consumer; // <== Spring main class

  public Main(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    AutowireCapableBeanFactory acbFactory = context.getAutowireCapableBeanFactory();
    acbFactory.autowireBean(this);
  }

  public static void main(String[] args) {

    Main main = new Main();
    main.consumer.consume();
  }
}

The ‚miraculous‘ part happens in the constructor. Retrieving the application-context, getting an AutowireCapableBeanFactory and then tell the factory to resolve all autowired dependencies in this class. The ugly part here is the reference escape of this in the constructor.

For completeness

<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
                      http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/context 
                      http://www.springframework.org/schema/context/spring-context-2.5.xsd">

  <context:component-scan base-package="ch.sahits.test.spring"/>

</beans>

sake here the applicationContext.xml:

 

Schreibe einen Kommentar