1. 引言
在 Spring 框架中,Bean 是由 Spring IoC 容器管理的对象,是整个依赖注入机制的核心。开发者通常需要将业务组件注册到容器中,才能让 Spring 统一管理对象的创建、装配和生命周期。那么,将 Bean 放入 Spring 容器中的方式到底有哪些?本文将从XML 配置、注解扫描、Java 配置类、自动装配等角度系统梳理,并给出可运行的代码示例。
2. 基于 XML 配置的方式
XML 配置是 Spring 最传统、最经典的 Bean 注册方式,适用于早期 Spring 项目或需要精细控制 Bean 定义细节的场景。
2.1 使用 <bean> 标签
在 applicationContext.xml 中,通过 <bean> 标签显式声明一个 Bean:
xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.example.service.UserService"/>
</beans>
2.2 构造器注入与属性注入
xml
<bean id="userService" class="com.example.service.UserService">
<constructor-arg ref="userDao"/>
<property name="timeout" value="5000"/>
</bean>
2.3 使用 XML 命名空间简化配置
Spring 还提供了 p 命名空间和 c 命名空间,简化属性与构造器参数的书写:
xml
<bean id="userService" class="com.example.service.UserService"
p:userDao-ref="userDao"
c:timeout="5000"/>
3. 基于注解的方式
随着 Spring 2.5 引入注解支持,基于注解的 Bean 注册成为主流,代码更加简洁。
3.1 组件扫描注解
在类上标注 @Component 及其派生注解,并配合 <context:component-scan> 或 @ComponentScan 开启扫描:
java
@Component
public class UserService {
// 业务逻辑
}
@Component 的常用派生注解包括:
@Service:标注业务层组件@Repository:标注数据访问层组件@Controller:标注控制层组件@Configuration:标注配置类组件
3.2 开启组件扫描
在 XML 中开启:
xml
<context:component-scan base-package="com.example"/>
或在配置类中开启:
java
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
4. 基于 Java 配置类的方式
Spring 3.0 起,Java 配置类成为推荐的 Bean 注册方式,完全摆脱 XML。
4.1 使用 @Bean 注解
在 @Configuration 类中,通过 @Bean 方法显式注册 Bean:
java
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService(userDao());
}
@Bean
public UserDao userDao() {
return new UserDao();
}
}
4.2 @Bean 方法之间的依赖
Spring 会拦截 @Bean 方法调用,保证返回的是容器中的单例 Bean,而非新实例:
java
@Bean
public UserService userService() {
return new UserService(userDao()); // 这里返回的是容器中的 userDao 单例
}
5. 基于自动装配的方式
自动装配(Autowiring)让 Spring 根据类型或名称自动注入依赖,减少显式配置。
5.1 @Autowired 按类型注入
java
@Component
public class OrderService {
@Autowired
private UserService userService;
}
5.2 @Resource 按名称注入
java
@Component
public class OrderService {
@Resource(name = "userService")
private UserService userService;
}
5.3 构造器注入(推荐)
java
@Component
public class OrderService {
private final UserService userService;
public OrderService(UserService userService) {
this.userService = userService;
}
}
6. 使用 BeanFactoryPostProcessor 与 BeanPostProcessor 动态注册
对于需要动态注册 Bean 的场景,可以通过编程方式向容器注册。
6.1 使用 DefaultListableBeanFactory 动态注册
java
@Component
public class DynamicBeanRegistrar implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(UserService.class);
factory.registerBeanDefinition("userService", definition);
}
}
6.2 使用 ImportBeanDefinitionRegistrar 批量注册
java
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("userService",
new GenericBeanDefinition() {{
setBeanClass(UserService.class);
}});
}
}
7. 使用 @Import 导入配置类
@Import 可以将普通类或配置类导入容器,常用于模块化装配。
java
@Configuration
@Import({UserService.class, AppConfig.class})
public class MainConfig {
}
8. 使用 FactoryBean 创建复杂 Bean
当 Bean 的创建过程复杂(如涉及代理、连接池)时,可以实现 FactoryBean 接口:
java
@Component
public class UserServiceFactoryBean implements FactoryBean<UserService> {
@Override
public UserService getObject() {
return new UserService();
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
9. 方式对比与选型建议
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| XML 配置 | 遗留项目、第三方 Bean 定义 | 配置集中、无需改代码 | 繁琐、类型不安全 |
| 注解扫描 | 业务组件注册 | 简洁、开发效率高 | 依赖包扫描路径 |
| Java 配置类 | 第三方库 Bean、复杂装配 | 类型安全、可编程 | 配置类需维护 |
| 自动装配 | 依赖注入 | 减少样板代码 | 隐式依赖、调试困难 |
| 动态注册 | 框架扩展、条件装配 | 灵活、运行时可控 | 复杂度高 |
| FactoryBean | 复杂对象创建 | 封装创建细节 | 需额外实现接口 |
选型建议:
- 业务组件优先使用
@Component系列注解 +@ComponentScan - 第三方库或需要精细配置的 Bean 使用
@Bean配置类 - 遗留系统维护使用 XML
- 框架级扩展使用
BeanFactoryPostProcessor或ImportBeanDefinitionRegistrar
10. 总结
将 Bean 放入 Spring 容器的方式可以归纳为以下六类:
- XML 配置 :
<bean>标签显式声明 - 注解扫描 :
@Component系列 +@ComponentScan - Java 配置类 :
@Configuration+@Bean - 自动装配 :
@Autowired/@Resource/ 构造器注入 - 编程式注册 :
BeanFactoryPostProcessor/ImportBeanDefinitionRegistrar FactoryBean:封装复杂对象的创建逻辑
在实际开发中,注解扫描 + Java 配置类 是最主流的组合方式,兼顾了开发效率与类型安全。理解这些注册方式,有助于深入掌握 Spring IoC 容器的运作原理,也能在遇到 Bean 未注册、重复注册等问题时快速定位原因。