java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring注解@Configuration与@Component

揭秘Spring核心注解@Configuration与@Component的本质区别

作者:一勺菠萝丶

在Spring框架中,@Configuration和@Component都是常用的注解,但它们有着本质的区别,本文将深入剖析这两者的核心区别,并通过代码示例展示它们的实际行为差异

引言:Spring中的两个关键角色

在Spring框架中,@Configuration@Component都是常用的注解,但它们有着本质的区别。许多开发者在使用时容易混淆它们的行为,特别是当涉及@Bean方法时。本文将深入剖析这两者的核心区别,并通过代码示例展示它们的实际行为差异。

核心区别一览表

场景被标记的类本身类内部调用 @Bean 方法
@Component/@Service 类默认单例每次调用都创建新对象
@Configuration 类默认单例调用其他 @Bean 方法返回单例

这个表格揭示了Spring中一个关键但常被误解的区别:类本身的单例行为与类内部方法调用的单例行为是不同的概念

场景一:业务组件(@Component/@Service)

类本身的单例行为

@Service // 等同于 @Component
public class UserService {
    // 业务逻辑...
}

类本身是单例

Spring容器只会创建一个UserService实例

通过@Autowired注入时

注入的是同一个实例

@Controller
public class UserController {
    @Autowired 
    private UserService service1; // 单例
    
    @Autowired 
    private UserService service2; // 同一个单例
    
    public void checkSingleton() {
        System.out.println(service1 == service2); // 输出 true
    }
}

关键特性

场景二:配置类中的@Bean方法调用

危险示例:在@Component中调用@Bean方法

@Component // 错误用法!
public class DatabaseConfig {
    
    @Bean
    public DataSource dataSource() {
        System.out.println("创建新的DataSource实例");
        return new HikariDataSource(); // 创建连接池
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate() {
        // 直接调用 → 每次创建新连接池!
        return new JdbcTemplate(dataSource()); 
    }
}

问题所在

当Spring调用jdbcTemplate()方法时:

直接执行dataSource()方法

每次调用都new HikariDataSource() → 创建多个连接池

输出结果:

创建新的DataSource实例
创建新的DataSource实例

根本原因

安全解决方案

方案一:使用@Configuration代理保护

@Configuration // 关键!
public class CorrectConfig {
    @Bean
    public A a() {
        return new A(b()); // ✅ 代理确保总返回同一实例
    }
    
    @Bean
    public B b() { 
        System.out.println("创建B实例");
        return new B(); 
    }
}

代理机制

Spring通过CGLIB代理增强@Configuration

单例保护

多次调用b()返回同一个实例

输出结果:

创建B实例 // 仅打印一次

方案二:使用方法参数注入(推荐)

@Configuration // 或 @Component
public class BestConfig {
    @Bean
    public A a(B b) { // ✅ 通过参数注入单例
        return new A(b);
    }
    
    @Bean
    public B b() { return new B(); }
}

为什么会有这种差异?

1.@Component/@Service类

2.@Configuration类

实际应用场景

正确使用@Configuration

@Configuration
public class AppConfig {
    // 全局单例的基础设施
    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:mysql://localhost/db");
        return ds;
    }
    
    // 安全调用其他@Bean方法
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

正确使用@Component

@Service
public class OrderService {
    // 业务方法
    public void processOrder(Order order) {
        // 业务逻辑...
    }
}

常见错误及修复

错误示例

@Component // 错误!应该用@Configuration
public class PaymentConfig {
    @Bean
    public PaymentService paymentService() {
        // 每次创建新验证器 → 破坏单例
        return new PaymentService(validator()); 
    }
    
    @Bean
    public Validator validator() { 
        return new PaymentValidator();
    }
}

修复方案

@Configuration // 修复方法1:改为@Configuration
public class PaymentConfig {
    @Bean
    public PaymentService paymentService(Validator validator) { // 修复方法2:参数注入
        return new PaymentService(validator);
    }
    
    @Bean
    public Validator validator() { 
        return new PaymentValidator();
    }
}

终极总结

“被@Component或@Service标记的类本身默认是单例的,@Autowired注入时不会创建新对象。

但在@Component类内部调用@Bean方法时,会像普通Java方法一样执行,每次调用都创建新实例。

而@Configuration类通过CGLIB代理,确保跨@Bean方法调用时始终返回单例。”

这个区别反映了Spring的两种不同机制:

结语

理解@Configuration@Component的本质区别对于构建健壮的Spring应用至关重要。记住以下黄金法则:

面试总结

"在 @Configuration 类中,所有 @Bean 方法都会CGLIB 代理。当在同一个配置类中调用其他 @Bean 方法时,Spring 会确保始终返回同一个单例实例。而在 @Component 类中,直接调用 @Bean 方法会像普通 Java 方法一样执行,每次调用都创建新实例,破坏单例性。

到此这篇关于揭秘Spring核心注解@Configuration与@Component的本质区别的文章就介绍到这了,更多相关Spring注解@Configuration与@Component内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文