Spring Basics: @Conditional Bean Wiring

Table of Contents

A Use Case for Conditional Bean Wiring

Suppose we want to enable caching for our web application. We are interested to find out whether this helps our application or whether the overhead will so big that it actually slows our application down. We decide we want to put the new functionality behind a feature toggle. We add a new property to our application called app.caching.enabled=false. When we are ready to enable the caching, we change the property’s value to true and we are in business. When the results disappoint, we can easily revert the property’s value to false.

@Conditional Introduction

The @Conditional annotation can be used on any type or method that declares a bean:

@Conditional(SomeCondition.class)
@Bean
public SomeBean someBean() {
    return new SomeBean();
}

@Conditional takes a mandatory class that implements the functional interface Condition. Condition defines a single method matches that returns a boolean. The method decides whether the bean should be loaded into the Spring context or whether it should be ignored:

public SomeCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        /* make decision based on context and metadata */
    }
}

A @Conditional @Configuration

When @Conditional is applied to a class which is also annotated with @Configuration, then all of the @Bean methods, @Import, @ComponentScan and other annotations will be subject to the condition. This means that when the condition evaluates to false, all of the configuration defined in that class will be ignored.

Since creating a cache carries costs with it (even an unused cache reserves space on the heap for its initial size), this is exactly what we want in our situation.

@Configuration
@Conditional(CacheCondition.class)
public class CacheConfig {

    /* ... define cache manager and caches here ... */

    public static CacheCondition implements ConfigurationCondition {
        public ConfigurationPhase getConfigurationPhase() {
            return ConfigurationPhase.PARSE_CONFIGURATION;
        }

        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return context.getEnvironment().getRequiredProperty("app.caching.enabled", Boolean.class);
        }
    }
}

Since we are dealing with a conditional configuration instead of a regular bean, the condition class implements ConfigurationCondition instead of Condition. The ConfigurationCondition makes us implement another method that specifies at which point the condition should be evaluated. Normally, this ConfigurationPhase is set to REGISTER_BEAN, which means it checks the condition when adding a regular bean. In this case, we want it set to PARSE_CONFIGURATION, which makes Spring evaluate the condition at the moment the configuration class is parsed. If the condition does not match at that moment, the @Configuration class will not be added to the context.

Dependencies

The only dependency is on spring-context:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.1.RELEASE</version>
</dependency>

Sample Project

A sample project can be found on GitLab.

Spring Basics: Dynamically Inject Values With Spring’s @Value

Table of Contents

If we do not want to hard-code values into our source code, we can use properties files. With the @Value annotation, Spring gives us an easy means to get properties from properties files and inject them into our code.

Dependencies

This post was written with Spring 5.0.5.RELEASE and Java 1.8.0_181.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

Inject Scalars

Let’s say we have the following key-value pairs in a file app.properties:

app.string.property=hello
app.integer.property=987
app.floating.point.property=3.14159
app.boolean.property=false

To get access to these properties, we declare an application configuration in Java:

@Configuration
@ComponentScan
@PropertySource("app.properties")
public class AppConfig {}

The @PropertySource adds a property source to Spring’s Environment. Here, the properties file is placed in the root of the classpath (and since I am using Maven default paths, that would be src/main/resources.

package com.relentlesscoding.wirebeans;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class HabitTest {

    @Value("${app.string.property}")
    private String stringProperty;

    @Value("${app.integer.property}")
    private int integerProperty;

    @Value("${app.floating.point.property}")
    private float floatingPointProperty;

    @Value("${app.boolean.property}")
    private boolean booleanProperty;

    @Test
    public void stringProperty() {
        assertThat(stringProperty, is("hello"));
    }

    @Test
    public void integerProperty() {
        assertThat(integerProperty, is(987));
    }

    @Test
    public void floatingPointProperty() {
        assertThat(floatingPointProperty, is(3.14159F));
    }

    @Test
    public void booleanProperty(){
        assertThat(booleanProperty, is(false));
    }
}

Injecting Complex Values

Let’s say we also have collections of values in our properties file:

app.collection.strings.property=one, two, three
app.collection.floats.property=1.2, 3.4, 5.6
app.collection.dates.property=2018-09-30T10:00:00, 2018-09-29T11:00:00, 2018-09-28T12:00:00

By default, Spring won’t be able to interpret Lists of values. The best we can do, by default, is getting an array of Strings:

@Value("${app.collection.strings.property}")
private String[] stringArrayProperty;

@Test
public void stringsArrayProperty() {
    assertArrayEquals(new String[]{"one", "two", "three"}, stringArrayProperty);
}

Declare a ConversionService to Inject Collections And Other Complex Types

To convert properties to other types than strings, we need to enable Spring’s ConversionService.

@Bean
public static ConversionService conversionService() {
    return new DefaultFormattingConversionService();
}

We instantiate DefaultFormattingConversionService, which is “configured by default with converters and formatters appropriate for most applications”. That means that it can convert comma-separated strings to common, generic collection types, and can convert strings to dates, currencies and TimeZones, for example.

Notice that the method is declared static. ConversionService is of type BeanFactoryPostProcessor and must be instantiated very early in the Spring container lifecycle, so that its services are available when processing of annotations such as @Autowired and @Value are done. By declaring the method static, the ConversionService can be invoked without instantiating the enclosing @Configuration class. Therefore, we can use @Value (and other annotations) that make use of this converter in the same class, without running into the trouble that ConversionService is not yet instantiated.

To learn more about bean instantiation, read the part under “BeanFactoryPostProcessor-returning @Bean methods”. Also, this answer on StackOverflow is very clarifying.

@Test
public void floatArrayProperty() {
    assertArrayEquals(new float[]{1.2F, 3.4F, 5.6F}, floatArrayProperty, 0.01F);
}

To convert dates from a properties string to actual date objects we need an extra step: @DateTimeFormat. This annotation indicates the format of the date to Spring. In this case, we format our property strings to conform to ISO-8601 or DateTimeFormat.ISO.DATE_TIME:

@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@Value("${app.collection.dates.property}")
private List<LocalDate> listOfDatesProperty;

@Test
public void listOfDatesProperty() {
    assertThat(listOfDatesProperty.size(), is(3));
    assertEquals(LocalDate.parse("2018-09-30T10:00:00", DateTimeFormatter.ISO_DATE_TIME), listOfDatesProperty.get(0));
}

Thanks to our ConversionService, we can now convert not only to arrays, but also to collections.

Inject Maps With Spring’s @Value

If we throw in a Spring Expression Language (SpEL) expression, we can even have dictionaries in our properties and convert them to Maps:

app.collection.map.string.to.integer={one:"1", two:"2", three:"3"}
@Value("#{${app.collection.map.string.to.integer}}")
private Map<String, Integer> mapStringToInteger;

@Test
public void mapProperty() {
    assertThat(mapStringToInteger.size(), is(3));
    assertEquals(new Integer(1), mapStringToInteger.get("one"));
    assertEquals(new Integer(2), mapStringToInteger.get("two"));
    assertEquals(new Integer(3), mapStringToInteger.get("three"));
}

Notice the #{...} that delimits the SpEL expression. It evaluates the string that comes from the properties files and parses it to a Map. To understand how this works, let’s have a look at what a literal map in a SpEL expression would look like:

@Value("#{{1: 'Catch-22', 2: '1984', 3: 'Pride and Prejudice'}}")
private Map<Integer, String> books;

A literal map in a SpEL expression is delimited by braces {key: 'value', ...}. This is exactly what we had in our properties file.

Spring Basics: XML Setter Injection With Custom Method Names

In a previous blog post we ended up with the following XML-based configuration:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="firstStreak" class="com.relentlesscoding.wirebeans.PositiveStreak">
        <constructor-arg ref="startTime"/>
    </bean>
    <bean id="secondStreak" class="com.relentlesscoding.wirebeans.PositiveStreak">
        <constructor-arg ref="startTime"/>
    </bean>

    <util:list id="myRunningStreaks">
        <ref bean="firstStreak"/>
        <ref bean="secondStreak"/>
    </util:list>

    <bean id="running" class="com.relentlesscoding.wirebeans.Running">
        <property name="streaks" ref="myRunningStreaks"/>
    </bean>

</beans>

The complete code can be found on GitLab.

To recap, we instantiate a bean of type LocalDate by invoking its now() static method. We inject this bean into two PositiveStreak beans using constructor injection. We reference the LocalDate bean by its id, startTime. Next, we create a bean that is a list of PositiveStreaks using the util namespace. We then inject this list into a Habit class called Running using setter injection.

For setter injection to work from XML, the setter method needs to follow Java bean conventions. This means that there should be setter method called set<PropertyName>, where the important part is that the method start with set. The name of the property itself can be something completely different from the field it is setting, Spring does not care. For example, the following would work without a hitch:

class Running implements Habit {
    private List<Streak> myStreakList;

    public void setStreaks(List<Streak> aListOfStreaks) {
        this.myStreakList = aListOfStreaks;
    }
}
<bean class="com.relentlesscoding.wirebeans.Running">
    <property name="streaks" ref="myRunningStreaks" />
</bean>

The only important thing is that the name attribute of the property element corresponds to the Property part of setProperty setter method in Java. To reiterate: The name of the field that is being set by the mutator method can be something completely different from the name attribute of the property element.

But sometimes we want to be creative with our method names if that makes the code more readable, or maybe we are dealing with code that we cannot change. If we end up with a mutator method that does not start with set, you are now in a position to appreciate that Spring will not find this method and throws a BeanCreationException.

Handling Mutators With a Non-Standard Names

On StackOverflow a solution was proposed that uses a MethodInvokingFactoryBean. But since we are invoking a method that does not return a result, the documentation recommends we use MethodInvokingBean instead.

Suppose we have the follow setter in place:

class Running implements Habit {
    private List<Streak> myStreakList;

    public void replaceStreaks(List<Streak> aListOfStreaks) {
        this.myStreakList = aListOfStreaks;
    }
}

We could leverage a MethodInvokingBean like so:

<bean id="running" class="com.relentlesscoding.wirebeans.Running">

<bean id="caller" class="org.springframework.beans.factory.config.MethodInvokingBean">
    <property name="targetObject" ref="running"/>
    <property name="targetMethod" value="replaceStreaks"/>
    <property name="arguments" ref="myRunningStreaks"/>
</bean>

(Notice that we actually use setter injection on the MethodInvokingBean by specifying properties on it.) The targetObject attribute specifies on which instance a targetMethod should be invoked. We can specify our custom mutator name, and pass a reference to our arguments.

This is verbose, so think carefully before you violate the Java bean naming convention. Consider using a facade if the code is not under your control. Alternatively, Java-based Spring configuration with the @Autowired annotation will work on a method with any name.

Spring Basics: Wiring Beans with XML Configuration

If you have to work with legacy Spring applications, chances are you will have know how XML-based configuration works. Although Java configuration is preferred for new applications, sometimes you just don’t have a choice.

The Code

You can find the code from this blog post on GitLab.

Dependencies

The only dependency you need to get a Spring container running is spring-context. Add in Maven:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

Declaring Beans in XML

The following class will become a bean:

package com.relentlesscoding.wirebeans;

import java.util.List;

public class Running implements Habit {
    private final String name = "Running";
    private final String description = "Run 10 km every day";
    private List<Streak> streaks;

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public List<Streak> getStreaks() {
        return streaks;
    }

    public void setStreaks(List<Streak> streaks) {
        this.streaks = streaks;
    }

    public void addStreak(Streak streak) {
        streaks.add(streak);
    }
}

Create an XML file, give it any name (I would call it applicationContext.xml) and place it in your src/main/resources.

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

    <bean id="running" class="com.relentlesscoding.wirebeans.Running"/>
</beans>

This would wire a bean with the id running that is of type Running (which is a Habit). You can see why this is much more of a hassle to set up than using a simple @Component annotation on a class or a @Bean annotation in a @Configuration class. Those XML namespaces are nasty, but luckily most IDEs will help you with them.

Injecting Beans

To inject dependencies, we have two choices:

  • Constructor injection
  • Setter injection

Unlike with Java configuration, we cannot insert into fields when using XML-based configuration.

Constructor Injection

Suppose we want to insert one bean into another bean, for instance a HabitRepository that persists habits to the database into a HabitService. By using the <constructor-arg /> element and the ref property, we can accomplish this:

<bean id="habitRepository" class="com.relentlesscoding.wirebeans.HabitRepository" />

<bean id="habitService" class="com.relentlesscoding.wirebeans.HabitService">
    <constructor-arg ref="habitRepository" />
</bean>

If we wanted to pass more arguments to the HabitService constructor, we must keep on eye on the order: it must be the same as the order in which they are declared in the class.

To ease working with constructor arguments and as a way to curtail the verbosity of the XML configuration, Spring offers the c XML namespace to help wire beans without the need to create a sub-element <constructor-arg />:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="currentTime" class="java.time.LocalDate" factory-method="now" />

    <bean id="streak"
        class="com.relentlesscoding.wirebeans.PositiveStreak"
        c:startTime-ref="currentTime" />

We define a bean of type java.time.LocalDate and we use the static factory method now() to get an instance of it. We then use the c namespace to pass it to the constructor of our PositiveStreak bean. c:startTime-ref="currentTime" should be read as: pass the reference to the bean with id currentTime to the constructor argument that has the name startTime.

So we can reference constructor arguments by name. We can also reference them by position. c:_0-ref="currentTime" would do the exact same thing. XML does not allow a digit as the first character of an attribute, so we have to use an underscore. If there is only a single argument to the constructor, we can even use the shorthand c:_-ref="currentTime". I would not want to promote this as readable, but it’s good to know it exists and might be used in the wild.

Read more about the c namespace here.

Setter Injection

To use setter injection with XML-based configuration, you use the <property> element. If you look back at the Running class above, you see it has a method setStreak that takes a List<Streak>:

<bean id="startTime" class="java.time.LocalDate" factory-method="now" />

<bean id="streak"
    class="com.relentlesscoding.wirebeans.PositiveStreak"
    c:_0-ref="startTime" />

<bean id="running" class="com.relentlesscoding.wirebeans.Running">
    <property name="streaks">
        <list>
            <ref bean="streak" />
        </list>
    </property>
</bean>

The <property> element has an attribute name that refers to the field name of the bean being set. In the current case, class Running has a field named streaks. As a child element of <property> we define a list of Streak references.

For a list of literal String values, this would have looked like:

<property name="listOfStrings">
    <list>
        <value>string value 1</value>
        <value>string value 1</value>
        <value><null/></value>
    </list>
</property>

The list is wired with literal Strings and even a literal null.

Spring also provides the p namespace to make this more convenient (less verbose):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="streak"
        class="com.relentlesscoding.wirebeans.PositiveStreak"
        c:_0-ref="startTime" />

    <bean id="running"
        class="com.relentlesscoding.wirebeans.Running"
        p:streaks-ref="streak" />
</beans>

The usage of the p namespace is a lot like that of the c namespace. In this case, p:streaks-ref="streak" tells spring to wire a property named streaks with the bean that is referenced by the id streak. Now, the property streaks takes a List. If we pass only a single element to that list, the current syntax works and Spring will happily insert the single reference to streak into a List for us and pass that to the setter method. If we want to pass more than one element in a list, however, we have to create the list separately first, and then pass the id of that reference to the p property:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="startTime" class="java.time.LocalDate" factory-method="now"/>

    <bean id="firstStreak" class="com.relentlesscoding.wirebeans.PositiveStreak" c:_0-ref="startTime"/>
    <bean id="secondStreak" class="com.relentlesscoding.wirebeans.PositiveStreak" c:_0-ref="startTime"/>

    <util:list id="myRunningStreak">
        <ref bean="firstStreak"/>
        <ref bean="secondStreak"/>
    </util:list>

    <bean id="running" class="com.relentlesscoding.wirebeans.Running" p:streaks-ref="myRunningStreak"/>
</beans>

We need to add the util namespace and the location of the util schema definition to get this to work. You see that the XML becomes quite verbose the more you try to do with it. The util namespace allows us to create collections of literal values or beans. These collections can then be referenced by their id.

More information:

Taking the App for a Test Ride

package com.relentlesscoding.wirebeans;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class HabitTest {

    @Autowired
    Habit runningHabit;

    @Test
    public void runningHabitIsNotNull() {
        Assert.assertNotNull(runningHabit);
    }

    @Test
    public void runningHabitHasSingleStreak() {
        Assert.assertEquals(2, runningHabit.getStreaks().size());
    }
}

To run integration tests where the Spring context is available, you need the following dependency:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.5.RELEASE</version>
    <scope>test</scope>
</dependency>

spring-test contains the SpringRunner JUnit runner, and the @ContextConfiguration that will tell Spring where to look for the application context that contains the beans that need to be wired. In this case, we tell it to look at the applicationContext.xml that we put in src/main/resources, so we can reference it by looking at the root of our classpath with the attribute locations = "classpath:applicationContext.xml".

Spring Basics: Wiring and Injecting Beans with Java Configuration

For new projects, Java configuration is preferred over XML-based configuration. For XML-based configuration, see a future blog post.

The code

You can find the code from this blog post on GitLab.

Dependencies

The only dependency you need to get a Spring container running is spring-context. Add in Maven:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

Detecting beans by scanning for components

Create a class (which can have any name, here I chose AppConfig) and annotate it with @Configuration and @ComponentScan:

package com.relentlesscoding.wirebeans;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class AppConfig {}

By default, the @ComponentScan will recursively scan the package in which the AppConfig class is declared. To change this, you can add a package to the value element (@ComponentScan("com.relentlesscoding.wirebeans.beans")) to scan only that package. If it troubles you that this is not type safe and hinders refactoring (it is a simple string after all), you can also pass Class objects to the basePackageClasses element. It will then scan the package that class is part of. Some people even recommend to create marker interfaces in each package for this purpose, but I think this clutters up the source code too much.

Three ways to declare beans

Now Spring is able to detect our beans. We can declare beans in three ways:

  • By annotating a class with @Component.
  • By annotating a method with a non-void return type with @Bean in a class annotated with @Configuration.
  • By annotating any method with a non-void return type with @Bean.

Automatic configuration with @Component

The simplest way to declare a bean is by annotating a class with @Component:

@Component
public class Running implements Habit {
    private final String name;
    private final String description;
    private final List<Streak> streaks;

    // accessors omitted for brevity
}

If we do not specify the value element of @Component, the id of the bean will be the lowercase name of the class, in this case running. We can use this id elsewhere to specify this particular bean, should ambiguities arise.

Explicit configuration in class annotated with @Configuration

If you have control over the beans you are creating, i.e. you are writing the source code, you would always go with automatic configuration by annotating your bean classes with @Component. If you are creating a bean for a class from a library, you can define your beans in your AppConfig class:

@Configuration
@ComponentScan
public class AppConfig {

    @Bean
    public List<Streak> streaks() {
        List<Streak> streaks = new ArrayList<>();
        streaks.add(new PositiveStreak(LocalDate.now()));
        return streaks;
    }

}

Here, we defined a bean of type List<Streak> that we can now inject into any other bean by using the @Autowired annotation (see below).

Lite Beans

Actually, we can declare any method with a non-void return type to be a @Bean. If we declare a bean outside of a configuration class, it will become a “lite bean”. Spring will still manage its lifecycle and scope and we can still autowire the bean into other beans, but when invoking the method directly, it will just be a plain-old Java method invocation without Spring magic. (Normally, Spring would create a proxy around the bean and all invocations would go through the Spring container. This would mean that by default only a single instance of the bean would exist, for example. In “lite” mode, however, the annotated method is just a factory method, and will happily instantiate a new object every time it is called.)

Read more about lite beans here.

Using the declared beans

To use Spring’s dependency injection, you have a couple of options, all of which involve annotating a method or field with @Autowired. By default, a matching bean of the specified type needs to exist in the Spring context or else Spring will throw an exception. To make the injection optional, set the required element of @Autowired to false.

Constructor injection

For mandatory dependencies, you should use constructor injection. “Mandatory” means the bean would not make sense without the bean on which it depends. For example, a HabitService persists Habits to the database. So a DAO or repository would be a mandatory dependency.

@Component
public class HabitService {
    private final HabitRepository habitRepository;

    @Autowired
    public Running(HabitRepository habitRepository) {
        this.habitRepository = habitRepository;
    }
    ...
}

Field injection

Field injection should not be preferred, because it makes testing (e.g. with mocks) harder to pull off. In the following example we inject a dependency into a private field. When we would try to mock the dependency, we would have to deal with the access restriction.

@Component
public class HabitService {
    @Autowired private HabitRepository habitRepository;
    ...
}

Setter injection

A third way to inject dependencies is through setter injection. Putting @Autowired on any method with one or more parameters will make Spring look for appropriate bean candidates in the Spring context.

@Component
public class Running implements Habit {
    private final String name = "Running";
    private final String description = "Run 10 km every day";
    private List<Streak> streaks;

    @Autowired
    public setStreaks(List<Streak> streaks) {
        this.streaks = streaks;
    }
    ...
}

Taking the application for a test run

package com.relentlesscoding.wirebeans;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class HabitTest {

    @Autowired
    Habit runningHabit;

    @Test
    public void runningHabitIsNotNull() {
        Assert.assertNotNull(runningHabit);
    }

    @Test
    public void runningHabitHasSingleStreak() {
        Assert.assertEquals(1, runningHabit.getStreaks().size());
    }
}

We can specify the application context by using the @ContextConfiguration annotation and filling in the classes element. The JUnit 4 annotation @RunWith specifies the SpringRunner.class (which is a convenience extension of the longer SpringJUnit4ClassRunner).

@ContextConfiguration is part of the spring-test library:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.5.RELEASE</version>
    <scope>test</scope>
</dependency>