Skip to main content

Spring Configuration

Introduction

Spring Framework offers a powerful and flexible configuration system that allows developers to define how their application components (beans) are created, wired, and managed. For beginners, understanding Spring's configuration options is a fundamental step in mastering the framework.

In this guide, we'll explore the different approaches to Spring configuration:

  • XML-based configuration
  • Java-based configuration
  • Annotation-based configuration

Each method has its strengths, and modern Spring applications often use a combination of these approaches. By the end of this tutorial, you'll understand how to configure Spring applications effectively and choose the right approach for your specific needs.

XML-Based Configuration

XML configuration was Spring's original configuration mechanism. Despite newer alternatives, many existing applications still use XML configuration, making it important to understand.

Basic XML Configuration

A Spring XML configuration file typically looks like this:

xml
<?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 definitions go here -->
<bean id="customerService" class="com.example.services.CustomerServiceImpl">
<!-- Constructor-based dependency injection -->
<constructor-arg ref="customerRepository" />
</bean>

<bean id="customerRepository" class="com.example.repositories.JdbcCustomerRepository">
<!-- Property-based dependency injection -->
<property name="dataSource" ref="dataSource" />
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mydb" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
</beans>

Loading XML Configuration

To load and use an XML configuration file:

java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
public static void main(String[] args) {
// Load the Spring XML configuration
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

// Get a bean from the container
CustomerService customerService = context.getBean("customerService", CustomerService.class);

// Use the bean
customerService.findAllCustomers();
}
}

XML Configuration Features

XML configuration provides several features:

  1. Bean Definitions: Define beans with the <bean> element
  2. Dependencies: Configure dependencies through constructor arguments (<constructor-arg>) or setters (<property>)
  3. Collections: Configure collection types like lists, maps, and sets
  4. Bean Scopes: Define bean scopes (singleton, prototype, etc.) using the scope attribute
  5. Lifecycle Methods: Specify initialization and destruction methods

Example of collections and scopes:

xml
<bean id="customerService" class="com.example.CustomerService" scope="singleton">
<property name="supportedRegions">
<list>
<value>North America</value>
<value>Europe</value>
<value>Asia</value>
</list>
</property>
<property name="regionManagers">
<map>
<entry key="North America" value-ref="naManager" />
<entry key="Europe" value-ref="euManager" />
</map>
</property>
</bean>

Java-Based Configuration

Java-based configuration uses Java classes and annotations to configure the Spring container. This approach offers type safety, better refactoring capabilities, and IDE assistance.

Basic Java Configuration

Here's a simple Java configuration class:

java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.commons.dbcp2.BasicDataSource;

@Configuration
public class AppConfig {

@Bean
public CustomerService customerService() {
return new CustomerServiceImpl(customerRepository());
}

@Bean
public CustomerRepository customerRepository() {
JdbcCustomerRepository repository = new JdbcCustomerRepository();
repository.setDataSource(dataSource());
return repository;
}

@Bean(destroyMethod = "close")
public BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
}

Loading Java Configuration

To load a Java configuration:

java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
public static void main(String[] args) {
// Load the Java configuration
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// Get a bean from the container
CustomerService customerService = context.getBean(CustomerService.class);

// Use the bean
customerService.findAllCustomers();
}
}

Java Configuration Features

Java configuration offers several features:

  1. @Configuration: Marks a class as a source of bean definitions
  2. @Bean: Defines a bean to be managed by Spring
  3. @Import: Combines multiple configuration classes
  4. @Profile: Enables beans conditionally based on active profiles
  5. @PropertySource: Integrates property files

Example with additional features:

java
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;

@Configuration
@Import({SecurityConfig.class, PersistenceConfig.class})
@PropertySource("classpath:application.properties")
public class AppConfig {

@Bean
@Profile("development")
public DataSource developmentDataSource() {
// Development database configuration
}

@Bean
@Profile("production")
public DataSource productionDataSource() {
// Production database configuration
}
}

Annotation-Based Configuration

Annotation-based configuration minimizes explicit configuration by using annotations directly on your component classes, enabling Spring to auto-detect and configure beans.

Component Scanning

To enable annotation-based configuration, you need to configure component scanning:

In XML:

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

<context:component-scan base-package="com.example" />
</beans>

In Java:

java
@Configuration
@ComponentScan("com.example")
public class AppConfig {
// Configuration beans can still be defined here
}

Component Annotations

Spring provides several annotations to define components:

  1. @Component: Generic stereotype for any Spring-managed component
  2. @Service: Indicates the class is a service layer component
  3. @Repository: Indicates the class is a data access component
  4. @Controller: Indicates the class is a web controller

Example:

java
import org.springframework.stereotype.Service;

@Service
public class CustomerServiceImpl implements CustomerService {
private final CustomerRepository repository;

// Constructor injection
public CustomerServiceImpl(CustomerRepository repository) {
this.repository = repository;
}

@Override
public List<Customer> findAllCustomers() {
return repository.findAll();
}
}
java
import org.springframework.stereotype.Repository;

@Repository
public class JdbcCustomerRepository implements CustomerRepository {
private DataSource dataSource;

// Setter injection
@Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}

@Override
public List<Customer> findAll() {
// JDBC code to fetch customers
}
}

Dependency Injection Annotations

Spring provides several annotations for dependency injection:

  1. @Autowired: Injects a dependency (field, constructor, or setter)
  2. @Qualifier: Specifies which bean to inject when multiple candidates exist
  3. @Value: Injects values from properties files

Example:

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {
private final MessageSender messageSender;
private final String appName;

@Autowired
public NotificationService(@Qualifier("emailSender") MessageSender messageSender,
@Value("${application.name}") String appName) {
this.messageSender = messageSender;
this.appName = appName;
}

public void sendNotification(String recipient, String message) {
String formattedMessage = String.format("[%s] %s", appName, message);
messageSender.sendMessage(recipient, formattedMessage);
}
}

Combining Configuration Approaches

Modern Spring applications often combine different configuration approaches. For example:

  1. Java configuration for application-level beans and infrastructure
  2. Annotation-based configuration for components
  3. Properties files for environment-specific values

Here's an example of a mixed configuration:

java
@Configuration
@ComponentScan("com.example")
@PropertySource("classpath:application.properties")
@ImportResource("classpath:legacy-config.xml")
public class AppConfig {

@Bean
public DataSource dataSource(@Value("${db.url}") String url,
@Value("${db.username}") String username,
@Value("${db.password}") String password) {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}

In this example:

  • Java configuration defines the overall structure
  • Component scanning finds annotated components
  • Property values are injected from a properties file
  • Legacy XML configuration is imported with @ImportResource

Real-World Example: Building a Simple Customer Management System

Let's put everything together in a simple customer management system example:

Domain Objects

java
public class Customer {
private Long id;
private String firstName;
private String lastName;
private String email;

// Getters, setters, constructors
}

Repository Layer

java
public interface CustomerRepository {
List<Customer> findAll();
Customer findById(Long id);
void save(Customer customer);
void delete(Long id);
}

@Repository
public class JdbcCustomerRepository implements CustomerRepository {
private final JdbcTemplate jdbcTemplate;

@Autowired
public JdbcCustomerRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}

@Override
public List<Customer> findAll() {
return jdbcTemplate.query("SELECT id, first_name, last_name, email FROM customers",
(rs, rowNum) -> {
Customer customer = new Customer();
customer.setId(rs.getLong("id"));
customer.setFirstName(rs.getString("first_name"));
customer.setLastName(rs.getString("last_name"));
customer.setEmail(rs.getString("email"));
return customer;
});
}

// Other methods implementation
}

Service Layer

java
public interface CustomerService {
List<Customer> findAllCustomers();
Customer findCustomerById(Long id);
void saveCustomer(Customer customer);
void deleteCustomer(Long id);
}

@Service
public class CustomerServiceImpl implements CustomerService {
private final CustomerRepository customerRepository;

@Autowired
public CustomerServiceImpl(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}

@Override
public List<Customer> findAllCustomers() {
return customerRepository.findAll();
}

// Other methods implementation
}

Configuration

java
@Configuration
@ComponentScan(basePackages = "com.example")
@PropertySource("classpath:database.properties")
public class AppConfig {

@Bean
public DataSource dataSource(Environment env) {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}

@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}

Main Application

java
public class CustomerManagementApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

CustomerService customerService = context.getBean(CustomerService.class);

// Display all customers
System.out.println("All Customers:");
List<Customer> customers = customerService.findAllCustomers();
for (Customer customer : customers) {
System.out.println(customer.getFirstName() + " " + customer.getLastName());
}

// Add a new customer
Customer newCustomer = new Customer();
newCustomer.setFirstName("John");
newCustomer.setLastName("Smith");
newCustomer.setEmail("[email protected]");
customerService.saveCustomer(newCustomer);

System.out.println("\nCustomer added successfully!");
}
}

Summary

Spring configuration is a fundamental aspect of developing applications with the Spring Framework. In this guide, we've covered:

  • XML-based configuration: Traditional approach with explicit bean definitions
  • Java-based configuration: Type-safe configuration using Java classes and annotations
  • Annotation-based configuration: Lightweight approach using component scanning and annotations
  • Combined approaches: How to effectively mix different configuration styles

Each configuration approach has its strengths:

  • XML configuration provides clear separation between code and configuration
  • Java configuration offers type safety and refactoring support
  • Annotation-based configuration reduces boilerplate and promotes convention over configuration

Modern Spring applications typically use a combination of these approaches, with Java configuration and annotations being the most common choice for new projects.

Additional Resources and Exercises

Resources

Exercises

  1. Basic Configuration: Create a Spring application with Java configuration that defines a simple service and repository.

  2. Mixed Configuration: Extend the application to use component scanning for your services and repositories, while keeping explicit configuration for infrastructure components.

  3. Profile-Based Configuration: Configure your application to use different data sources based on profiles (e.g., "development" vs. "production").

  4. Property Externalization: Move all configurable properties to an external properties file and inject them using @Value annotations.

  5. Advanced Configuration: Create a more complex application with multiple modules, each with its own configuration class, and combine them using @Import.

By mastering Spring configuration, you'll build a solid foundation for developing robust and maintainable Spring applications!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)