What’s the Real Difference Between @Configuration‑Based @Bean and @Component in Spring?
The article demonstrates that @Configuration classes are CGLIB‑proxied, ensuring @Bean methods are intercepted so internal calls respect container singleton semantics, whereas @Component classes lack this proxy, causing each @Bean call to create a new instance, and explains when to use each approach.
First, two equivalent‑looking code snippets are shown: one class annotated with @Configuration and another with @Component, each defining @Bean methods for UserService and OrderService. Although the source looks identical, the runtime behavior differs dramatically.
0. How Spring manages beans
When the container starts it scans classes marked with @Configuration, @Component, @Service, @Repository, registers @Bean definitions, instantiates beans, injects dependencies and finally returns fully initialized instances.
1. Core of @Configuration: proxy + CGLIB enhancement
@Configurationclasses are wrapped with a CGLIB proxy; every @Bean method is intercepted. The proxy caches the result of the first invocation, so subsequent calls return the same instance.
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
System.out.println("userService() called");
return new UserService(orderService());
}
@Bean
public OrderService orderService() {
System.out.println("orderService() called");
return new OrderService();
}
}Running the application prints:
orderService() called
userService() calledOnly one call to orderService() occurs; the UserService receives the same OrderService instance that the container holds.
2. The truth about @Component: plain POJO, no proxy
When the class is annotated with @Component, the @Bean methods are ordinary Java methods. The container does not create a proxy, so an internal call creates a new object each time.
@Component
public class AppConfig {
@Bean
public UserService userService() {
System.out.println("userService() called");
return new UserService(orderService());
}
@Bean
public OrderService orderService() {
System.out.println("orderService() called");
return new OrderService();
}
}Output:
orderService() called
userService() called
orderService() called // second call!Thus UserService may hold a different OrderService instance from the one stored in the container.
3. Code verification – are the instances the same?
@Configuration
public class ConfigVsComponent {
@Configuration
static class ConfigConfig {
@Bean
public OrderService orderService() { return new OrderService(); }
@Bean
public UserService userService() { return new UserService(orderService()); }
}
@Component
static class ComponentConfig {
@Bean
public OrderService orderService() { return new OrderService(); }
@Bean
public UserService userService() { return new UserService(orderService()); }
}
public static void main(String[] args) {
// Test @Configuration
AnnotationConfigApplicationContext ctx1 = new AnnotationConfigApplicationContext(ConfigConfig.class);
UserService user1 = ctx1.getBean(UserService.class);
OrderService order1 = ctx1.getBean(OrderService.class);
System.out.println(user1.orderService == order1); // true
// Test @Component
AnnotationConfigApplicationContext ctx2 = new AnnotationConfigApplicationContext(ComponentConfig.class);
UserService user2 = ctx2.getBean(UserService.class);
OrderService order2 = ctx2.getBean(OrderService.class);
System.out.println(user2.orderService == order2); // false!
}
}4. Why @Configuration needs a proxy
The proxy guarantees that calls between @Bean methods obey the container’s singleton semantics. Without it, each call would create a fresh object, leading to multiple data sources, mismatched transactions, etc.
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() { return new HikariDataSource(); }
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource()); // proxy ensures same DataSource
}
@Bean
public TransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource()); // same instance
}
}5. When to prefer @Component + @Bean
If you need to control bean creation manually or the @Bean methods do not depend on each other, you can place them in a @Component class. The example shows independent beans such as RestTemplate and ObjectMapper.
@Component
public class ExternalServiceConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(5))
.build();
}
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}6. Common pitfalls
@Beanmethods do not have to be public; they can be protected or package‑private, but they must not be private or final when @Configuration(proxyBeanMethods = true) is used.
Mixing @Configuration and @Component in the same project is possible but discouraged because it makes the proxy behaviour hard to reason about.
The performance overhead of the proxy is negligible (nanoseconds) and is outweighed by the guarantee of correct singleton behavior.
Practical scenarios
Scenario 1 – multiple inter‑dependent beans
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.withUsername("admin")
.password(passwordEncoder.encode("123456"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
return provider;
}
}Scenario 2 – single independent bean
// Either @Component or @Configuration works
@Component // or @Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate() {
return new RedisTemplate<>();
}
}Conclusion: @Configuration creates a CGLIB‑enhanced proxy that preserves container semantics for inter‑bean calls, while @Component leaves @Bean methods as plain methods that may instantiate new objects on each call. For consistency and to avoid subtle bugs, the article recommends using @Configuration for all configuration classes.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
