Contents

A @Configuration class is itself a Spring bean. Each @Bean method is intercepted by Spring's CGLIB proxy so that calling the method multiple times always returns the same singleton instance.

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public DataSource dataSource() { HikariDataSource ds = new HikariDataSource(); ds.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb"); ds.setUsername("user"); ds.setPassword("pass"); ds.setMaximumPoolSize(10); return ds; } @Bean public JdbcTemplate jdbcTemplate() { // Calling dataSource() here returns the same singleton instance return new JdbcTemplate(dataSource()); } }

You can declare the dependency as a method parameter instead of calling a sibling method directly. Spring injects the correct bean automatically.

@Configuration public class ServiceConfig { @Bean public UserRepository userRepository(DataSource dataSource) { return new JdbcUserRepository(dataSource); } @Bean public UserService userService(UserRepository userRepository) { return new UserServiceImpl(userRepository); } }

Large applications can split their configuration into multiple classes and compose them with @Import.

import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import({DataSourceConfig.class, SecurityConfig.class, CacheConfig.class}) public class RootConfig { // Aggregates the other configuration classes into one root context }

Spring Boot extends the base @Conditional with convenient shortcuts. @ConditionalOnMissingBean registers a bean only if no other bean of that type has been defined — great for providing sensible defaults.

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class NotificationConfig { @Bean @ConditionalOnProperty(name = "notification.channel", havingValue = "email") public NotificationService emailNotificationService() { return new EmailNotificationService(); } @Bean @ConditionalOnMissingBean(NotificationService.class) public NotificationService noOpNotificationService() { return message -> System.out.println("NoOp: " + message); } }