Spring Wiring

Spring is a large framework with a lot of moving parts. In this post and a few follow up posts, I'm going to cover some of the major components of Spring and explain how they fit together.

Right now I'm just going to overview the ways that Spring allows for dependency injection. Later posts will cover other features of Spring.

Spring

Spring is an open source application framework for writing Java applications. At it's core, Spring allows for inversion of control (through dependency injection) and aspect oriented programming. It isn't only a framework for web applications, though it often gets used for web applications and has a number of extensions to support that.

Spring XML

Spring's XML configuration options are a pretty famous, or infamous, part of Spring's history.

I've come to use Spring quite recently so I've never had cause to use XML configuration, preferring Java-based configuration instead. I also don't have any intention of starting to use XML configuration. In this post I will focus on implicit wiring and Java-based configuration, though everything demonstrated can also be achieved with XML configuration if that's your preference.

Beans

Bean is an interesting word because it has a number of meanings in Java applications. For the purposes of most people using Spring, beans are just objects which are managed for us by Spring.

When Spring is managing a bean, it goes through the following stages:

  • Instantiate
  • populate properties (value and bean references)
  • BeanNameAware's setBeanName()
  • BeanFactoryAware's setBeanFactory()
  • ApplicationContextAware's setApplicationContext()
  • Pre-Initialization BeanPostProcessors
  • InitializingBean's afterPropertiesSet()
  • Call custom init-method
  • Post-Initialization BeanPostProcessors
  • DisposableBean's destroy
  • Call custom destroy method

The words in italics indicate interfaces which your beans can implement to have the callbacks invoked at the appropriate time.

Dependency Injection

Dependency injection frees objects from needing to manage their dependencies. When using dependency injection, objects only need to specify the interface of their dependencies, and allow something else to provide the correct dependencies.

This makes reuse much easier, as objects are now much more loosely coupled, and has big advantages for unit testing as it's much easier to mock out dependencies.

For example, without Dependency Injection:

public class TaskExecutor {
    private ConfigStore configStore = new S3ConfigStore();

    public TaskExecutor() {
    }
}

If your class needs access to some configuration object, you could do it as shown. In here we’re creating a TaskExecutor which creates a S3ConfigStore. You could imagine this config store allowing us to fetch content from S3.

But really, there's no reason for this class to know that the configuration is coming from S3. That's an implementation detail. As long as it can get a ConfigStore it doesn't really matter where the config comes from.

with Dependency Injection:

public class TaskExecutor {
    private ConfigStore configStore;

    public TaskExecutor(ConfigStore configStore) {
        this.configStore = configStore;
    }
}

If we instead wrote the class like this, the ConfigStore can be injected into the TaskExecutor. The major advantage to this is that it means we can swap in an alternative ConfigStore without changing the TaskExecutor.

In particular, this is useful if we want to test the TaskExecutor, as we can swap in a local config store, or a mocked config store, or whatever we need to make the test run. This way we’re not reliant on whatever ConfigStore the TaskExecutor has been hard-coded to use.

However, with this model we do have an issue - where does the ConfigStore come from? We could have it so that every time we create a TaskExecutor we also create a ConfigStore and inject it. That can work, but it doesn’t scale very well.

Spring Containers
The solution to this in Spring is containers.

All of the magic Spring provides for you comes from containers. These control the creation, configuration, and destruction of beans.

Spring containers fall into one of two groups. They are either Bean Factories (which allow for the creation of beans, and provides dependency injection), or Application Contexts (which include additional features such as loading properties files).

ConfigStore configStore = container.getBean(ConfigStore.class);

Whichever type of container you have, once it is created and has beans loaded, you can use the container.getBean(class) method to get a bean of the given type.

So… how do we get the beans loaded into a container?

Component Scanning

To get Spring to create the beans you want, you can either manually configure the beans, or instruct Spring to create the beans through component scanning. As much as possible you should prefer component scanning, as it's less explicit code, so I'll cover that first.

Component scanning is the method by which Spring can automatically figure out which beans need to be created.

@Configuration
@ComponentScan
class MyAppConfiguration {
    
}


@Component
public class S3ConfigStore implements ConfigStore {
}

We first label a class with the @Component annotation. Then we ensure that this class is scanned, by adding a @ComponentScan annotation to a class which already has the @Configuration annotation.

By default, @ComponentScan will scan the same package as the configuration class, but the package to be scanned can be changed using the basePackages or basePackageClasses arguments.

Manual Bean Creation

If component scanning isn't sufficient for your needs, for example if you're needing to inject dependencies on third party libraries, you'll need to turn to manually specifying the beans to be created.

@Configuration
class MyAppConfiguration {

    @Bean
    public AWSCredentialsProviderChain awsCredentialsProviderChain() {
        return new AWSCredentialsProviderChain(new DefaultAWSCredentialsProviderChain());
    }

    @Bean
    public DbMetadata dbMetadata(AppProperties appProperties) {
        return new DynamoDbMetadata(appProperties.getTableName());
    }
}

For this, our @Configuration classes can contain contain explicitly created beans, which will be loaded into the Spring context.

To make this work, you should create, inside your @Configuration class, a method which returns and instance of the class you want to wire up. This method should be annotated @Bean. The bean will be given the same name as the method, or the name argument to the annotation, if provided.

If these bean methods are called repeatedly (for example by your own code) Spring will intercept the calls and ensure that the same bean is returned for all calls.

If this bean depends on another bean, you can specify that bean specifically by calling the bean's method (e.g. AppProperties appProperties = appProperties()), or you can specify the class or interface that you need to be wired by specifying an argument in the method you are creating. Spring will ensure that this argument is set when calling the method.

Autowiring

In the previous example, the wiring up of AppProperties into dbMetadata is an example of autowiring, where Spring will ensure that dependencies are met. Having the AppProperties argument indicated to Spring that there was a dependency on AppProperties.

In other components, you should use the @Autowired annotation

You can apply an @Autowired annotation to any properties, constructors, or methods that you want to be wired up. Spring will search for any matching beans in the context - and if it finds exactly one - it will set the value (or call the method). If Spring finds 0 beans, and the required argument has not been set to false, it will throw an exception. If there is ambiguity (multiple beans could be wired) then an exception will also be thrown.

public class TaskExecutor {

    private ConfigStore configStore;

    @Autowired
    public TaskExecutor(ConfigStore configStore) {
        this.configStore = configStore;
    }
}

In this case, because there is a single constructor - the Autowired annotation could be skipped and the behaviour would be the same.

Ambiguous Bean Wiring

In the case of multiple beans fitting a requirement, this can be solved by making the requirements more strict, or by declaring one bean the primary choice.

@Configuration
class MyAppConfiguration {
    @Primary
    @Bean
    public AWSCredentialsProviderChain awsCredentialsProviderChain() {
        return new AWSCredentialsProviderChain(new DefaultAWSCredentialsProviderChain());
    }
}

The @Primary annotation can be applied to a class which has @Component applied, or a method which has @Bean applied, to specify that this bean should be the primary choice in case of conflict. If two primary beans are in conflict, then an exception will be thrown.

@Configuration
class MyAppConfiguration {

    @Bean
    public AWSCredentialsProviderChain awsCredentialsProviderChain() {
        return new AWSCredentialsProviderChain(new DefaultAWSCredentialsProviderChain());
    }

    @Bean
    public AWSSystem awsSystem(@Qualifier("awsCredentialsProviderChain") AWSCredentialsProviderChain credentials) {
       // ...
    }

}

Alternatively, the @Qualifier annotation can be added to a method or variable annotated with @Autowired. A string can be provided as an argument to @Qualifier to specify that only the bean with the given qualifier be autowired.

By default a bean's qualifier is its id (which is by default the name of the method or class that created it).

@Configuration
class MyAppConfiguration {

    @Bean
    @Qualifier(“live-credentials”)
    public AWSCredentialsProviderChain awsCredentialsProviderChain() {
        return new AWSCredentialsProviderChain(new DefaultAWSCredentialsProviderChain());
    }

    @Bean
    public AWSSystem awsSystem(@Qualifier(“live-credentials") AWSCredentialsProviderChain credentials) {
       // ...
    }

}

If the automatically generated qualifier isn’t enough - you can specify the qualifier by using the @Qualifier annotation on the method or class that creates the bean.

Profiles

Sometimes we want different beans to be active in different environments. For example we may want to use an in-memory database during development and a connection to a Postgres database in production. To avoid having to recompile the code between environment changes (potentially introducing bugs), Spring allows for a concept of "profiles" which are set at runtime.

@Configuration
@Profile("test")
class MyAppConfiguration {

}


@Bean
@Profile("dev")
public AWSCredentialsProviderChain awsCredentialsProviderChain() {
    return new AWSCredentialsProviderChain(new DefaultAWSCredentialsProviderChain());
}


@Component
@Profile("live")
public class S3ConfigStore implements ConfigStore {
    
}

To indicate that a bean, component, or configuration class is only to be used by a certain profile, apply the @Profile annotation.

@ActiveProfiles("dev")
public class TestComponentA extends TestCase {
}

-Dspring.profiles.active=dev

When running your application, set the -Dspring.profiles.active parameter to set the active profile, or during tests use the @ActiveProfiles annotation.

Conditional Beans

You can also create an implementation of the Conditional class, which has a matches method that returns true if the bean should be created and false otherwise.

This condition can be used for the creation of beans by using the @Conditional annotation and pointing it to your Conditional implementation.

class DynamoDBConfiguredCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return (System.getProperty("dynamodb.port") != null);
    }
}

@Bean
@Conditional(DynamoDBConfiguredCondition.class)
public DynamoDB dynamoDB() {
    
}

The matches method is passed a ConditionContext and a AnnotatedTypeMetadata. The ConditionContext allows for looking into the current context: created beans, resources on the classpath, etc. The AnnotatedTypeMetadata lets the method respond to other annotations on the same bean.

Bean Scopes

By default, every bean is a singleton, and if it is in injected in multiple places the same bean will be injected. This can be changed using the @Scope annotation.

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LRUCache {

}

@Component
@Scope(WebApplicationContext.SCOPE_SESSION)
public class ShoppingCart {

}

@Component
@Scope(ConfigurableBeanFactory.SCOPE_REQUEST)
public class RandomGenerator {

}

The most generally useful alternative scopes are ConfigurableBeanFactory.SCOPE_PROTOTYPE which means that each injection will result in a new instance, WebApplicationContext.SCOPE_SESSION which means that each web session will result in a new instance, and ConfigurableBeanFactory.SCOPE_REQUEST which means that each web request results in a new instance.

If you mix singleton beans with session or request scoped beans, you will eventually run into a problem where a singleton needs to refer to the currently active instance of another bean. To deal with this, Spring can inject a proxy of the bean which relays calls to the currently active instance. To use this see the proxyMode argument of @Scope.

That's all for this quick overview of Spring wiring and dependency injection. It's not organised as well as it could be but I hope it'll serve as a useful reference for some of the less used parts of Spring's dependency injection tools.

Show Comments

Get the latest posts delivered right to your inbox.