本文将详细讲解 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)