DEV Community

Hunor Vadasz-Perhat
Hunor Vadasz-Perhat

Posted on

spring-003: @Configuration-in-depth

@Configuration in Spring Framework: An In-Depth Explanation

The @Configuration annotation is part of the Spring Framework and is used to mark a class as a source of bean definitions. This annotation is critical in Spring's Java-based configuration, allowing developers to configure the application context without XML.

When a class is annotated with @Configuration, Spring treats it as a configuration class and processes it to generate and manage Spring Beans. Such classes typically contain one or more methods annotated with @Bean, which define the beans that should be managed by the Spring container.


Core Concepts of @Configuration

  1. Marks a Class as a Configuration Class

    The class becomes a source of bean definitions that Spring will use to set up the application context.

  2. Proxy Mechanism

    Spring generates a CGLIB-based subclass (proxy) of the class to ensure that @Bean methods return the same singleton bean instance by default. This behavior is called full mode. If not proxied, calling an @Bean method multiple times might create multiple instances.

  3. Integration with Component Scanning

    When used alongside @ComponentScan (or included in a class annotated with @SpringBootApplication), @Configuration-annotated classes can define beans explicitly while letting Spring automatically scan and register others.

  4. Allows Dependency Injection

    @Configuration classes support constructor-based or field-based dependency injection to resolve dependencies needed for bean creation.


Basic Example

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

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

    @Bean
    public MyController myController() {
        return new MyController(myService());
    }
}
Enter fullscreen mode Exit fullscreen mode
  • @Bean Methods: Define the beans explicitly.
  • Singleton Behavior: Even though myController() calls myService(), the MyService bean is created only once due to proxying.

Best Practices

1. Modular Configuration

Split configuration into multiple classes based on functionality, such as DataConfig, ServiceConfig, and WebConfig. This improves readability and maintainability.

@Configuration
public class DataConfig {
    @Bean
    public DataSource dataSource() {
        // Configure and return the data source
    }
}

@Configuration
public class ServiceConfig {
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Avoid Hardcoding Values

Use external configuration sources like application.properties or application.yml and inject values using @Value or @ConfigurationProperties.

@Configuration
public class AppConfig {

    @Value("${app.name}")
    private String appName;

    @Bean
    public AppService appService() {
        return new AppService(appName);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Leverage @ComponentScan for Scanning

Instead of defining all beans explicitly, use @ComponentScan to register components like @Service, @Repository, and @Component.

@Configuration
@ComponentScan(basePackages = "com.example.myapp")
public class AppConfig {
    // Explicit beans if necessary
}
Enter fullscreen mode Exit fullscreen mode

4. Use Conditional Beans

Define beans conditionally using annotations like @ConditionalOnProperty or @Profile to load beans only in specific environments or configurations.

@Configuration
public class AppConfig {

    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        // Development data source
    }

    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        // Production data source
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Organize Application Properties

Group configuration properties using @ConfigurationProperties to minimize scattered @Value annotations.

@Configuration
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    private String name;
    private String version;

    // Getters and setters
}
Enter fullscreen mode Exit fullscreen mode

What to Watch Out For

  1. Avoid Instantiating Beans Manually Never use new to create a bean inside a @Configuration class, as it bypasses Spring's dependency injection and lifecycle management.

Incorrect:

   @Bean
   public MyService myService() {
       return new MyServiceImpl(); // Manual creation
   }

   @Bean
   public MyController myController() {
       return new MyController(new MyServiceImpl()); // Creates a new instance
   }
Enter fullscreen mode Exit fullscreen mode
  1. Circular Dependencies Be cautious when defining beans that depend on each other, as it can lead to circular dependency issues.

Solution: Refactor the code to inject a Provider or use @Lazy.

  1. Overloading @Bean Methods
    Avoid overloading methods annotated with @Bean as it can lead to unintended results.

  2. Proxying Limitations
    The proxy mechanism of @Configuration works only if the class is not final. Avoid marking configuration classes as final.

  3. Use @Component Wisely
    Avoid mixing @Component and @Configuration in the same class. This can lead to unexpected behavior as @Configuration is processed differently.


Advanced Example with Dependency Injection

@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
                .url("jdbc:mysql://localhost:3306/mydb")
                .username("user")
                .password("password")
                .build();
    }

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

    @Bean
    public UserRepository userRepository(JdbcTemplate jdbcTemplate) {
        return new UserRepositoryImpl(jdbcTemplate);
    }

    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserServiceImpl(userRepository);
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Dependency Injection: Each bean depends on another and Spring resolves dependencies automatically.
  • Reusable Beans: Beans like DataSource and JdbcTemplate are reusable across multiple services.

Summary

  • Purpose: @Configuration enables defining beans in a centralized and type-safe manner.
  • Best Practices: Modularize configuration, use externalized properties, and leverage Spring's annotations like @Profile and @Conditional.
  • Pitfalls to Avoid: Instantiating beans manually, circular dependencies, overloading @Bean methods, and using final with @Configuration.

By following these practices, you can use @Configuration effectively to build robust and maintainable Spring applications.

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more