Java configuration: @Configuration, @Bean, @Import, @Profile and @Conditional
There are two ways to register beans:
- Component scanning: annotate your own classes with
@Component,@Service,@Repositoryor@Controller.@SpringBootApplicationscans the package of the main class and everything below it. @Beanmethods in a@Configurationclass: for classes you don't own (RestClient, ObjectMapper, a third-party SDK) or that need construction logic.
Important details:
- In a
@Configurationclass (full mode), Spring proxies the class so that one@Beanmethod calling another returns the same singleton.@Configuration(proxyBeanMethods = false)(lite mode, used by Spring Boot's own auto-configuration) skips that proxy: inject dependencies as method parameters instead of calling the methods. @Importpulls in other configuration classes;@ComponentScancustomises scanning.@Profile("dev")registers a bean only when that profile is active.@Conditionaland Spring Boot's@ConditionalOnProperty,@ConditionalOnClassand@ConditionalOnMissingBeandecide at startup whether a bean is registered. That is how auto-configuration works, and why defining your own bean replaces Boot's default.
Example
@Configuration
public class InfrastructureConfig {
@Bean
Clock clock() {
return Clock.system(ZoneId.of("Asia/Kolkata")); // inject Clock instead of calling now() directly
}
@Bean
RestClient paymentsClient(RestClient.Builder builder, @Value("${payments.url}") String url) {
return builder.baseUrl(url).build(); // a class we don't own: configure it here
}
@Bean
@Profile("dev")
DemoDataLoader demoData(CourseRepository courses) { // only when the "dev" profile is active
return new DemoDataLoader(courses);
}
@Bean
@ConditionalOnProperty(name = "features.sms", havingValue = "true")
Notifier smsNotifier(SmsClient client) { // only if features.sms=true
return new SmsNotifier(client);
}
}@Configuration // full mode: the class is proxied
class ReportsConfig {
@Bean Clock clock() { return Clock.systemUTC(); }
@Bean ReportService reports() {
return new ReportService(clock()); // returns the SAME Clock bean, thanks to the proxy
}
}
@Configuration(proxyBeanMethods = false) // lite mode: faster startup, no proxy
class ReportsConfigLite {
@Bean Clock clock() { return Clock.systemUTC(); }
@Bean ReportService reports(Clock clock) { // take it as a parameter; calling clock() would create a new one
return new ReportService(clock);
}
}Common mistake
Calling one @Bean method from another in a lite-mode configuration (or a @Component class): you get a brand-new object instead of the singleton.
Under the hood
Spring Boot's auto-configuration classes are listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports and guarded by conditions. Run with --debug (or check the Actuator conditions endpoint) to see which ones matched and why. Most of them use @ConditionalOnMissingBean, so declaring your own bean of the same type quietly switches Boot's version off.
Check yourself
Which packages does @SpringBootApplication scan by default?
How this connects
Know these first
Where this leads
You've reached the end of this thread. Try a learning path for what's next.
Was this lesson helpful?
Finished reading? Mark it complete to track your progress.