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);
}
}