本文将详细讲解 Spring 框架中
- Bean 的创建方式
- Bean 之间依赖注入(wiring)
这两种不同维度问题。
1. Spring Bean 的创建方式
在 Spring 框架中,Bean 是由 Spring 容器管理的基本对象。可以通过以下三种主要方式创建 Bean:
  
  
  1.1 使用 @Component 注解
通过在类上添加 @Component 注解,Spring 会自动扫描并将该类注册为一个 Bean。这种方式适用于直接定义一个类作为 Bean 的场景。
import org.springframework.stereotype.Component;
@Component
public class UserService {
    public void sayHello() {
        System.out.println("Hello from UserService!");
    }
}
- 特点:简单直接,适合业务逻辑类。
- 使用场景:当类是独立的、具有明确职责的组件时。
- 
注意:需要确保 @ComponentScan开启,且类在扫描路径内。
  
  
  1.2 使用 @Bean 注解在配置类中定义
在 @Configuration 注解的配置类中,通过 @Bean 注解定义方法返回值作为 Bean。这种方式适合需要自定义 Bean 实例化逻辑的场景。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}
- 特点:灵活,允许在方法中添加初始化逻辑。
- 使用场景:需要控制 Bean 的创建过程或配置第三方库的 Bean。
- 
注意:@Bean方法通常在@Configuration类中定义,以确保 Spring 代理正确处理。
1.3 使用 XML 配置
通过 XML 配置文件定义 Bean,Spring 容器会根据 XML 配置创建 Bean。这种方式在早期 Spring 项目中常见,现在较少使用。
<bean id="userService" class="com.example.UserService"/>
- 特点:配置文件集中管理,适合传统项目。
- 使用场景:遗留系统或需要与 XML 配置集成的场景。
- 注意:XML 配置较为繁琐,维护成本较高。
2. Bean 之间的依赖注入(Wiring)
Bean 的创建解决了单个 Bean 的定义问题,而依赖注入(Dependency Injection, DI)则解决了 Bean 之间的联动问题。Spring 提供了多种方式实现 Bean 之间的依赖注入,下面介绍四种主要方式。
  
  
  2.1 使用 @Autowired 注解
@Autowired 是 Spring 提供的自动注入注解,支持以下三种注入方式:
2.1.1 构造器注入
通过构造器注入依赖,推荐在需要强制依赖的场景中使用。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class OrderService {
    private final UserService userService;
    @Autowired
    public OrderService(UserService userService) {
        this.userService = userService;
    }
    public void processOrder() {
        userService.sayHello();
        System.out.println("Order processed!");
    }
}
- 优点:依赖不可变,强制注入,适合关键依赖。
- 缺点:构造器参数过多时代码显得冗长。
2.1.2 Setter 方法注入
通过 Setter 方法注入依赖,适合可选依赖或需要动态更换依赖的场景。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class OrderService {
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}
- 优点:灵活,允许运行时更改依赖。
- 缺点:依赖可能未初始化,需额外检查。
2.1.3 字段注入
直接在字段上使用 @Autowired 进行注入,代码简洁但不推荐。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class OrderService {
    @Autowired
    private UserService userService;
}
- 优点:代码简洁,开发效率高。
- 缺点:难以测试,依赖隐藏,容易导致空指针问题。
- 建议:避免使用字段注入,优先选择构造器注入。
2.2 使用 XML 配置进行注入
通过 XML 文件配置 Bean 之间的依赖关系,适合传统项目或需要集中管理配置的场景。
<bean id="userService" class="com.example.UserService"/>
<bean id="orderService" class="com.example.OrderService">
    <property name="userService" ref="userService"/>
</bean>
- 特点:集中式配置,适合复杂依赖关系。
- 使用场景:遗留系统或需要与 XML 集成的项目。
- 注意:XML 配置维护成本高,建议优先使用注解。
  
  
  2.3 在 @Bean 方法中手动注入
在 @Configuration 类中,通过 @Bean 方法手动指定依赖关系,适合需要自定义注入逻辑的场景。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
    @Bean
    public OrderService orderService() {
        OrderService orderService = new OrderService();
        orderService.setUserService(userService());
        return orderService;
    }
}
- 特点:高度灵活,允许复杂的初始化逻辑。
- 使用场景:需要控制依赖注入过程或整合第三方库。
- 注意:手动注入需要开发者确保依赖正确性。
  
  
  2.4 使用 @Resource 或 @Inject(补充)
除了 @Autowired,Spring 还支持 JSR-250 的 @Resource 和 JSR-330 的 @Inject 注解,用于依赖注入。它们与 @Autowired 类似,但匹配规则和优先级略有不同。
- 
@Resource:按名称匹配,优先于类型。
- 
@Inject:与@Autowired类似,但需要额外的依赖(如javax.inject)。
3. 创建与注入的维度区别
Bean 的创建和依赖注入属于 Spring 容器管理的两个不同维度:
- 
Bean 创建:关注如何定义和实例化单个 Bean,解决“Bean 是什么”的问题。包括:
- 
@Component:自动扫描和注册。
- 
@Bean:手动定义和配置。
- XML:通过配置文件声明。
 
- 
- 
依赖注入:关注 Bean 之间的关系,解决“Bean 如何协作”的问题。包括:
- 
@Autowired:自动注入(构造器、Setter、字段)。
- XML:通过配置文件指定依赖。
- 
@Bean方法:手动注入依赖。
 
- 
类比:
- 创建 Bean 就像生产零件(定义单个组件)。
- 依赖注入就像组装零件(将组件连接成一个整体)。
4. 综合示例
以下是一个完整的示例,展示如何结合不同的创建和注入方式。
4.1 代码示例
// UserService.java
import org.springframework.stereotype.Component;
@Component
public class UserService {
    public void sayHello() {
        System.out.println("Hello from UserService!");
    }
}
// OrderService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class OrderService {
    private final UserService userService;
    @Autowired
    public OrderService(UserService userService) {
        this.userService = userService;
    }
    public void processOrder() {
        userService.sayHello();
        System.out.println("Order processed!");
    }
}
// AppConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
    @Bean
    public OrderService orderService() {
        OrderService orderService = new OrderService(userService());
        return orderService;
    }
}
4.2 XML 配置示例
<beans>
    <bean id="userService" class="com.example.UserService"/>
    <bean id="orderService" class="com.example.OrderService">
        <constructor-arg ref="userService"/>
    </bean>
</beans>
4.3 测试代码
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        OrderService orderService = context.getBean(OrderService.class);
        orderService.processOrder();
    }
}
5. 总结与建议
- 
Bean 创建:
- 优先使用 @Component和@Bean,因为它们更符合现代 Spring 开发习惯。
- XML 配置适合遗留系统或特殊场景。
 
- 优先使用 
- 
依赖注入:
- 推荐使用构造器注入(@Autowired),确保依赖不可变且易于测试。
- 避免字段注入,减少潜在的空指针风险。
- 对于复杂依赖关系,可使用 @Bean方法手动注入。
 
- 推荐使用构造器注入(
- 
选择依据:
- 小型项目:使用 @Component和@Autowired(构造器注入)。
- 复杂项目:结合 @Bean和@Configuration实现灵活配置。
- 遗留项目:保留 XML 配置,逐步迁移到注解方式。
 
- 小型项目:使用 
🧭 配置方式的演进:从 XML 到注解,到最终的组合实践
回顾我们上面讲到的各种配置方式与注入方式,它们并不是孤立存在的,而是随着 Spring 框架本身的发展不断演化出来的解决方案。
🔍 为什么会出现这么多种配置方式?
- 早期阶段(Spring 1.x - XML 配置) 
 Spring 最初强调“配置即一切”,所以 XML 是唯一入口,所有对象、依赖、AOP 都写在 XML 中。好处是解耦、灵活,但冗长、易出错。
- 中期转型(Spring 2.5 - 注解驱动) 
 为减少 XML 配置,Spring 引入- @Component、- @Autowired等注解,推崇“约定优于配置”,降低了上手门槛,但也增加了控制权下放的风险(开发者必须清楚依赖图)。
- 成熟阶段(Spring 3.0+ - JavaConfig) 
 Spring 推出- @Configuration + @Bean,让你用 Java 编程的方式管理 Bean 定义,既保留了注解的灵活性,也重新获得了对配置的可控性与清晰度。
- 现代推荐(Spring Boot) 
 Spring Boot 在此基础上加入自动配置(AutoConfiguration),让大多数常见组件都能“开箱即用”。你几乎不用配置就能跑起来,但在复杂场景下,还是要理解底层配置机制。
💡 对实际项目的几点建议
- 新项目尽量用构造器注入 + JavaConfig( - @Configuration)组合。
 它语义清晰、IDE 友好、适合重构和测试。
- 字段注入虽然简单,但慎用在核心业务 Bean 上。 
 它让依赖关系隐形,调试、测试都更麻烦,Spring 团队也不推荐。
- 理解 XML 配置的原理,但别再用它开发新项目。 
 有时读旧项目源码、集成老系统还是会遇到它,但没必要继续写它。
- 注解方式适合配合框架做扫描注入,但别滥用。 
 比如你想注册一个第三方 SDK Bean,最好在- @Configuration中用- @Bean明确声明,而不是用- @Component暴力扫入。
🧩 最终的理解:不是“哪种最好”,而是“在什么场景用什么最合适”
Spring 提供了这么多方式,并不是让你“全都会用”,而是希望你能根据项目规模、团队习惯、架构复杂度来做出权衡。
而掌握它们的前提,是理解每一种配置/注入机制的本质和设计动因——这也正是这篇文章的初衷 😊
 

 
    
Top comments (0)