dagblog.
← Archive

Spring IoC and the trouble with mixing apples and pears


After a long absence, longer than it should have been, I decided to write something a little more practical. It deals with mixing managed and unmanaged classes when using the Spring Framework, a problem you may run into sooner or later. Given the late hour at which I’m writing, I’ll show the route I think is right and leave a full account of the feature for another day.

First, let’s clarify a few terms. For a managed class, Spring’s IoC container controls instance creation and its lifecycle. When you want an instance of a managed class, you ask the container, which takes care of dependency injection and the rest. An unmanaged class is instantiated by calling its constructor outside the Spring IoC container.

Most Spring applications consist of managed classes up to a central point, such as a controller or view helper in a web application, where they meet the world of unmanaged classes. If an application has only partly been ported to Spring, though, you have to work out how to obtain managed instances from unmanaged classes.

Suppose we have an unmanaged class MyObject that needs a managed class A to do its work. For some reason MyObject can’t be managed. How do we get it an instance of A?

public class MyObject {
private A a;
}

We’d probably put some code in the constructor or the object’s initialization method, if it has one, to obtain a managed instance of A from the Spring IoC container, perhaps through SingletonBeanFactoryLocator.

public class MyObject {
private A a;

public MyObject() {
BeanFactoryLocator bfl = ContextSingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bf = bfl.useBeanFactory("foo");
this.a = (A) bf.getFactory().getBean("a");
}
}

The code above works, but it introduces two problems.

  • The Spring IoC container has to be initialized so there will always be a BeanFactory under the key foo.
  • As soon as a managed instance B uses an unmanaged instance MyObject (B -> MyObject -> A), we can have a serious problem. If the container creates managed B before managed A, at best the program ends with a NullPointerException. At worst, initialization throws a cryptic exception that takes another two hours to decipher. The reason is simple. The IoC container has no information that B depends on A because MyObject is unmanaged.

I put these problems under the heading of mixing apples and pears, meaning inversion of control and a service locator. So how do we get out of it?

A quick fix is to establish an explicit dependency between managed classes B and A. Add a depends-on attribute to the XML deployment descriptor. This guarantees the right initialization order even for managed classes that otherwise have no relationship in the metadata.

It works, but it’s awkward to maintain. Another option combines metadata and AOP. We describe MyObject’s dependencies with metadata, either XML or annotations, and use AOP to set them after the constructor runs. Then whenever anyone calls new MyObject(), they get an instance with its dependencies set correctly. The metadata also makes the dependency between B and A visible.

Here’s the outline.

@Configurable
public class MyDomainObject {
@Autowired
private A a;
}
With the annotation org.springframework.beans.factory.annotation.Autowired I create the metadata that defines the dependencies. With the annotation org.springframework.beans.factory.annotation.Configurable I mark the class so the aspect can attach to it. All that remains is to instruct Spring so the pieces fit together. See the documentation, Using AspectJ to dependency inject domain objects with Spring.
An article worth reading on this topic is  New Improvements in Domain Object Dependency Injection Feature. There’s one detail developers often forget. This approach requires weaving, the process that inserts the aspect in the right places so it can work its dependency injection magic.

Medium version

Related articles

Related CZ Podcast episodes