将 Bean 放入 Spring 容器中的方式有哪些?

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 容器的方式可以归纳为以下六类:

  1. XML 配置 :<bean> 标签显式声明
  2. 注解扫描 :@Component 系列 + @ComponentScan
  3. Java 配置类 :@Configuration + @Bean
  4. 自动装配 :@Autowired / @Resource / 构造器注入
  5. 编程式注册 :BeanFactoryPostProcessor / ImportBeanDefinitionRegistrar
  6. FactoryBean:封装复杂对象的创建逻辑

在实际开发中,注解扫描 + Java 配置类 是最主流的组合方式,兼顾了开发效率与类型安全。理解这些注册方式,有助于深入掌握 Spring IoC 容器的运作原理,也能在遇到 Bean 未注册、重复注册等问题时快速定位原因。

相关推荐
java_nnnn1 小时前
LeetCode刷题滑动窗口(水果成篮+找到字符串中所有字母的异位词)Java
java·开发语言·leetcode
用户3721574261351 小时前
Java 读取、修改和清空 PDF 文档属性(元数据)
java
Mk598781 小时前
一个 Markdown 文件就是一个员工 —— AgentScope-Java 多智能体编排实战(核心篇)
后端
SL_staff1 小时前
从‘改一行代码等三天’到‘改完即生效’:实施工程师的逻辑引擎避坑手记
java·产品·全栈
小羊没烦恼!1 小时前
异步编程:线程概述及使用
java·开发语言·mysql·算法·c#
hhwyqwqhhwy1 小时前
设备树之 provider 和 consumer
java·linux·前端
\光辉岁月/1 小时前
9.日期时间类
java·date·localdatetime·日期类
用户3721574261351 小时前
Java 操作 PPT 超链接:添加、修改和删除
java
imDwAaY1 小时前
微信运动每天晚上 10 点推送排行榜,技术上到底有多难?
后端