How to Spoof Empty Collection injection in Spring

When we write libraries or frameworks, we sometimes have a class Foo whose constructor takes a Collection of Bars, where the Bars will be supplied by dependents.

For example:

@Named
public class Foo {

    private final List<Bar> bars;

    @Inject
    public Foo(List<Bar> bars) {
        this.bars = Collections.unmodifiableList(
                new ArrayList<>(bars));
    }

    // Implementation details.
}

Since the constructor is annotated with Inject, it implies that at least one Bar bean must be configured or else Spring will throw an UnsatisfiedDependencyException caused by a NoSuchBeanDefinitionException.

But what if an empty Collection is valid input?

It can be hard to coax String into supporting the zero-Bars case, but not impossible. I will demonstrate several ways it can be done, but some are better than others.

Use @Resource on the Collection Field (Not Recommended)

We could use the javax.annotation.Resource annotation. This tends to be the default response to “how do I Autowire a collection?”, time, after time, after time.

Using @Resource, Foo would look like this:

@Named
public class Foo {

    @Resource(name = "barList")
    private final List<Bar> bars = Collections.emptyList();

    public Foo() { }
}

Then, dependents would supply a List bean named barList or not, if they want the zero-Bar case.

This is not recommended and is my least favorite solution because we are now forcing dependents to use a DI framework; the barList cannot be set any other way.

Rely on Default Constructor Fallback (Not Recommended)

I first saw this solution in a post by Bruno Tavares. The crux of this solution is: if Spring cannot satisfy any @Autowired(required=false) constructors, it falls back to the default constructor. To apply this to our example, we would make the following changes to Foo:

@Named
public class Foo {

    private final List<Bar> bars;

    public Foo() {
        this(Collections.emptyList());
    }

    @Autowired(required = false)
    public Foo(List<Bar> bars) {
        this.bars = Collections.unmodifiableList(
                new ArrayList<>(bars));
    }
}

While this will produce the desired effect, this solution is limited.

First, it cannot be used when Foo depends on more than just a collection of Bars:

@Named
public class Foo {

    private final List<Bar> bars;
    private final Baz baz;

    // Problem: we cannot satisfy the Baz dependency.
    public Foo() {
        this(Collections.emptyList(), null);
    }

    @Autowired(required = false)
    public Foo(List<Bar> bars, Baz baz) {
        this.bars = Collections.unmodifiableList(
                new ArrayList<>(bars));
        this.baz = baz;
    }
}

We could work around this limitation by creating a setBaz method, but that would be bad design (for several reasons). There are better options, as we’ll see below.

Second, this solution restricts our DI framework to Spring since Java’s @Inject does not have an equivalent to @Autowired‘s required parameter.

For these two reasons, this solution is not recommended.

Use a “NOP” (Recommended, when applicable)

A “NOP” in this case would be an instance of Bar that has no effect on Foo. Foo doesn’t have to do any special reasoning about the NOP Bar (otherwise it wouldn’t be a NOP).

For example, suppose Bars are instances of java.util.Function, and Foo applies the list of Functions to some given value. In this case, the NOP is Function.identity() since the identity function would not effect the output of Foo.

Our configuration might look like this:

@Configuration
@ComponentScan("foo.bar") // Picks up Foo automatically
public class FooConfiguration {
    /**
     * Configure a single Bar value
     * so that Foo can be instantiated.
     */
    @Bean
    public Bar nop() {
        return Bar.NOP;
    }
}

When there is a sensible NOP Bar, this solution is the easiest to implement. Unfortunately, not all Bars will have a NOP value. Luckily, a final solution is available.

Create a Placeholder Value and Remove in a BeanPostProcessor (Recommended)

While this solution is more complex than the others, I think it maintains a separation of concerns: it isolates the business logic from the configuration and it does not force dependents to use a particular DI framework or any DI framework at all. In this version, a placeholder Bar value is added to the Configuration and is removed in a BeanPostProcessor, like so:

// Nice, clean Foo class.
@Named
public class Foo {

    private final List<Bar> bars;

    @Inject
    public Foo(List<Bar> bars) {
        this.bars = Collections.unmodifiableList(
                new ArrayList<>(bars));
    }

    // Getters

    public List<Bar> getBars() {
        return bars;
    }

    // OR Withers

    public Foo withBars(Collection<Bar> newBars) {
        return new Foo(newBars);
    }
}

And the configuration:

@Configuration
@ComponentScan("foo.bar") // Picks up Foo automatically
public class FooConfiguration {

    private final Bar placeholder = new Bar();

    /**
     * Configure a single Bar value (removed below)
     * so that Foo can be instantiated.
     */
    @Bean
    public Bar placeholder() {
        return placeholder;
    }

    /**
     * Remove the placeholder from Foo
     */
    @Bean
    public BeanPostProcessor fooPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(
                    Object o,
                    String s) {
                return o;
            }

            @Override
            public Object postProcessAfterInitialization(
                    Object o,
                    String s) {
                if (o instanceof Foo) {
                    // Remove the placeholder value.
                    List<Bar> bars = new ArrayList<>(
                            ((Foo) o).getBars());
                    bars.remove(placeholder);
                    return Foo.withBars(bars);
                } else {
                    return o;
                }
            }
        };
    }
}

Notice that this solution also requires us to add getters and/or withers to Foo. If adding a getter or wither for the Collection is not desirable, reflection black magic might be necessary:

@Override
public Object postProcessAfterInitialization(
        Object o,
        String s) throws BeansException
{
    if (o instanceof Foo) {
        try {
            Field barsField = Foo.class.getDeclaredField("bars");
            barsField.setAccessible(true);
            @SuppressWarnings("unchecked")
            List<Bar> bars = new ArrayList<>(
                    (List<Bar>) barsField.get(o));
            bars.remove(placeholder);
            return new Foo(bars);
        } catch (NoSuchFieldException
                | IllegalAccessException e) {
            throw new BeanCreationException(
                    "Could not create Foo",
                    e);
        }
    } else {
        return o;
    }
}

Final Thoughts

One of our goals when designing our libraries and frameworks should be to make them configuration independent. First, we shouldn’t force dependents to use a DI framework. One way we can do this by avoiding field injection. Second, we should not limit dependents’ choice of DI framework. We do this by using Java’s DI annotations, @Inject and @Named instead of framework specific ones such as Spring’s @Autowired and @Component. The final solution accomplishes all of these goals.

Maybe none of these solutions will meet your exact needs, but I hope they’ve given you ideas to create your own, perfect solution.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s