/ Java

Spring Tutorials - Spring Configuration

Back to Spring Tutorials main page

Spring Logo

Overview

This tutorial will look at how to configure the Spring Framework, or more specifically how to configure and bootstrap the Spring container.

How to load configuration values into your Spring application will be covered in another tutorial.

The Spring container can be configured in three ways. It can either read the configuration from an XML file, a Groovy file or it can be configured using Java annotations. Today the use of files based configuration is often considered antiquated, but it might be that the use of files is better from an ops perspective. You can also use both approaches in parallel.

In the previous tutorial on we saw how to use an XML configuration file to configure the container. In the examples below we will look at other configuration possibilities. We will only use ApplicationContext type containers in the examples and not the BeanFactory, even though you can do most of the same things with that container.

The steps we follow in this example are:

  1. Create Maven project for the example
  2. Create a simple Spring Bean class
  3. Create a bootstrap class for starting a container using annotation based configuration
  4. Start the container and retrieve a bean from the container to use in the application
  5. Import other configuration classes and files
  6. Use the component scan feature to autodiscover beans
  7. Use configuration profiles do make applications environment aware
The source code for this tutorial can be found on GitHub:
https://github.com/acntech/spring-tutorials/tree/develop/spring-configuration-tutorial

Setup

The example code was created using the following tools and frameworks:

We start out by creating a Maven project for our example, with the Spring dependency defined.

The Maven pom.xml file ends ut looking like this (example source code):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>no.acntech.tutorials</groupId>
    <artifactId>spring-configuration-tutorial</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- PROPERTIES -->
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <slf4j.version>1.7.21</slf4j.version>
        <logback.version>1.1.7</logback.version>
        <groovy.version>2.4.7</groovy.version>
        <spring.version>4.3.3.RELEASE</spring.version>
    </properties>

    <!-- DEPENDENCIES -->
    <dependencies>
        <!-- Logging -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy</artifactId>
            <version>${groovy.version}</version>
        </dependency>

        <!-- Spring Framework -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>

We get the familiar Maven project layout:

.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── no
    │   │       └── acntech
    │   │           └── tutorial
    │   │               └── spring
    │   │                   ├── bean
    │   │                   │   ├── AnnotatedSpringBean.java
    │   │                   │   └── SimpleSpringBean.java
    │   │                   ├── BootstrapAnnotationBasedApplicationContext.java
    │   │                   ├── BootstrapGroovyBasedApplicationContext.java
    │   │                   ├── BootstrapProfileAwareApplicationContext.java
    │   │                   ├── BootstrapXMLBasedApplicationContext.java
    │   │                   └── config
    │   │                       ├── ImportedSpringConfig.java
    │   │                       ├── SpringConfig.java
    │   │                       └── TestSpringConfig.java
    │   └── resources
    │       ├── beans.xml
    │       ├── logback.xml
    │       ├── spring.groovy
    │       └── spring.xml
    └── test
        └── java

Example

We start by creating a simple Spring bean class.

package no.acntech.tutorial.spring.bean;

public class SimpleSpringBean {

    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

Annotated configuration

Next we need to create the Spring configuration class where the declaration of the Spring Bean will be defined. The @Configuration annotation is used to mark this class as a Spring configuration class. Inside such a class we can define Spring beans by creating factory methods for each bean we want to define, each of the methods marked with a @Bean annotation.

package no.acntech.tutorial.spring.config;

import no.acntech.tutorial.spring.bean.SimpleSpringBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {

    @Bean
    public SimpleSpringBean simpleSpringBean() {
        return new SimpleSpringBean("Hello Annotation Based Spring Configuration!");
    }
}

This is the same as using the bean element inside an XML configuration file, like we saw in the previous tutorial.

<?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-4.3.xsd">

   <bean id="simpleSpringBean" class="no.acntech.tutorial.spring.bean.SimpleSpringBean">
       <property name="message" value="Hello XML Based Spring Configuration!"/>
    </bean>
</beans>

Though not as common, you can also use Groovy based file configuration to configure your container. Using Groovy the configuration above will look like this:

import no.acntech.tutorial.spring.bean.SimpleSpringBean

beans {
    simpleSpringBean(SimpleSpringBean) {
        message = "Hello Groovy Based Spring Configuration!";
    }
}

Now we need to create a bootstrap main method so that we can start a container, based on the annotated configuration class.

package no.acntech.tutorial.spring;

import no.acntech.tutorial.spring.bean.SimpleSpringBean;
import no.acntech.tutorial.spring.config.SpringConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class BootstrapAnnotationBasedApplicationContext {

    private static final Logger LOGGER = LoggerFactory.getLogger(BootstrapAnnotationBasedApplicationContext.class);

    public static void main(String[] args) {
        LOGGER.info("Bootstrapping Spring Application Context");

        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

        SimpleSpringBean simpleSpringBean = context.getBean(SimpleSpringBean.class);
        LOGGER.info("Spring says: {}", simpleSpringBean.getMessage());
    }
}

If we run the main method in this class we get the following output:

2016-10-22 08:42:19,303 [main] INFO  n.a.t.spring.BootstrapBeanFactory - Bootstrapping Spring Application Context
2016-10-22 08:42:19,722 [main] INFO  n.a.t.spring.BootstrapBeanFactory - Hello Annotation Based Spring Configuration!

We see that the container creates a Spring bean from the definition, and we get the expected log output.

To use XML or Groovy based configuration we simply need to change from using the AnnotationConfigApplicationContext class to instead use one for XML:

ApplicationContext context = new GenericXmlApplicationContext("spring.xml");

or Groovy:

ApplicationContext context = new GenericGroovyApplicationContext("spring.groovy");

Both the annotation based and the file based ApplicationContext classes can take several configuration classes or files as input, if you wish to spread the configuration into several parts.

Importing configuration

Another way to achieve separation of configuration is to import configuration from one class or file into another. For annotation based configuration you use the @Import annotation in the following way:

...
@Import({ImportedSpringConfig.class})
@Configuration
public class SpringConfig {
...
}

Here ImportedSpringConfig is another configuration class. The @Import annotation takes a single configuration class or an array of configuration classes to import into the context.

Using import in XML configuration is similarly:

<?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-4.3.xsd">

    <import resource="beans.xml"/>
</beans>

Here beans.xml is another XML configuration file.

You can also import XML configuration files from an annotated configuration class:

...
@ImportResource("classpath:spring.xml")
@Configuration
public class SpringConfig {
...
}

Or configuration classes from an XML configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:context="http://www.springframework.org/schema/context"
       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-4.3.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <context:annotation-config/>

    <bean class="no.acntech.tutorial.spring.config.SpringConfig">
</beans>

Be aware of the presence of the annotation-config element in the XML configuration file. This is a flag that tells the Spring container to recognize annotated configuration classes. Without it the configuration class will not be loaded.

Component scanning

Up until now we have explicitly defined Spring beans in the XML files, Groovy files or annotated configuration classes. When the application scales this approach becomes tedious and harder to maintain. Luckily the Spring Framework provides a feature to automatically discover Spring beans and wire them into your application when the container starts.

This feature is called component scanning, and is used in the following way. Annotation based configurations can use the @ComponentScan annotation on one of the configuration classes to specify where to scan for Spring beans.

package no.acntech.tutorial.spring.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan("no.acntech.tutorial.spring.bean")
@Configuration
public class ImportedSpringConfig {
}

Here we see that the @ComponentScan annotation takes a string parameter which is the Java package namespace from where Spring should look for Spring beans. Any Spring bean found under this package and sub-packages will be wired into the container.

Using XML configuration you define component scanning as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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-4.3.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <context:component-scan base-package="no.acntech.tutorial.spring.bean"/>
</beans>

For a Java bean to be defines as a Spring bean using the auto-discovery approach it must be annotated with a component annotation, which means one of the following stereotype annotations:

  • @Component - A generic Spring bean
  • @Controller - Typically a web controller, or backing bean for web apps
  • @RestController - A specialization of @Controller for REST enabled web apps
  • @Service - A service is typically the home of business logic
  • @Repository - A data repository, typically a database DAO

A Spring bean will then look like:

...
@Component
public class AnnotatedSpringBean {
...
}

Configuration profiles

Often it is necessary to differentiate you application configuration based on certain criteria, such as which runtime environment is used or other considerations. Typically this is because you often want a different setup when running in a development environment, a system test environment or a production environment.

Spring offers configuration profiles which can be use to make the application environment aware. Both Spring beans and whole configurations can be marked to only be relevant if run with one or more active profile.

The @Profile annotation is used to mark a configuration class with a profile:

package no.acntech.tutorial.spring.config;

import no.acntech.tutorial.spring.bean.SimpleSpringBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Profile("test")
@Configuration
public class TestSpringConfig {

    @Bean
    public SimpleSpringBean simpleSpringBean() {
        return new SimpleSpringBean("Hello Test Profile Spring Configuration!");
    }
}

The configurations in this class will only be invoked if the test profile is active, which in this case means that the SimpleSpringBean will be available in the context.

For XML based configurations you use the profile XML attribute:

<?xml version="1.0" encoding="UTF-8"?>
<beans profile="test" 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-4.3.xsd">
...
</beans>

You can also choose to only mark a single Spring bean with a profile:

...
@Configuration
public class TestSpringConfig {

    @Profile("test")
    @Bean
    public SimpleSpringBean simpleSpringBean() {
        return new SimpleSpringBean("Hello Test Profile Spring Configuration!");
    }
}
...

This is achieved in XML using nested beans elements:

<?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-4.3.xsd">

    <beans profile="prod">
        <bean id="simpleSpringBean" class="no.acntech.tutorial.spring.bean.SimpleSpringBean">
            <property name="message" value="Hello Test Profile Spring Configuration!"/>
        </bean>
    </beans>
</beans>

If using component scanning you can just mark the bean itself with the profile:

...
@Profile("prod")
@Component
public class AnnotatedSpringBean {
...
}

Both the @Profile annotation and the profile XML attribute supports making one or more profile as an array, respectively like @Profile({"prod", "test"}) and profile="prod, test".

Until now I have only told you how to mark configurations with a profile. But how do you indicate which profile(s) should be active when the application starts?

There are several ways to set a profile as active. One common way is use a JVM argument:

-Dspring.profiles.active="prod"

This can either passed directly to the java command or as part of the JAVA_OPTS environment variable.

You can also set the JVM property grammatically:

System.setProperty("spring.profiles.active", "prod");

If you use an application.properties file in your application then you can add a property to that file:

spring.profiles.active=prod

You can set the property straight into the environment object of the Spring application context:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("prod");

For web applications you can use servlet context parameters defined in the web.xml file:

<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>test</param-value>
</context-param>

Or programmatically:

servletContext.setInitParameter("spring.profiles.active", "test");

Be aware that Spring has an order for configuration properties. This means that you can define the same property (such as spring.profiles.active) in more than one way, and the property defines with the most significant order will override others.

Finally, for unit tests you can use the @ActiveProfiles annotation to activate configuration profiles:

package no.acntech.tutorial.spring.config;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.cache.CacheManager;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { SpringConfig.class })
@ActiveProfiles("test")
public class SimpleSpringBeanTest {
...
}
As you understand Spring offers a large variety on how to configure your application. Please be pragmatic when choosing your configuration approach so that it is easy to understand and debug.

Back to Spring Tutorials main page