The full picture
@Configuration // full mode: CGLIB proxy
class AppConfig {
@Bean DataSource dataSource() { return buildHikari(); }
@Bean JdbcTemplate jdbc() {
return new JdbcTemplate(dataSource()); // ✅ SAME singleton — call intercepted
}
}
@Configuration(proxyBeanMethods = false) // lite mode
class LiteConfig {
@Bean DataSource dataSource() { return buildHikari(); }
@Bean JdbcTemplate jdbc() {
return new JdbcTemplate(dataSource()); // ❌ NEW DataSource! plain method call
}
// lite done right: declare the dependency as a parameter
@Bean JdbcTemplate jdbcOk(DataSource ds) { return new JdbcTemplate(ds); }
}- Stereotypes are semantic, not cosmetic:
@Repositoryadds exception translation;@Service/@Controllermark architectural roles for humans, AOP pointcuts, and tooling. @Beanalso fits: objects needing constructor logic, third-party classes you can't annotate, and definitions that must be conditional (@ConditionalOn…in auto-configurations).- Boot's own auto-configurations use
proxyBeanMethods=false+ parameter injection everywhere — copy that idiom: it's faster and AOT/native-friendly, and parameter injection sidesteps the trap entirely.