将 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 未注册、重复注册等问题时快速定位原因。

相关推荐
非优秀程序员2 小时前
第07章 孙德良的阳谋|《互联网产品恩仇录》
javascript·后端
非优秀程序员2 小时前
第06章 浙江帮的内战|《互联网产品恩仇录》
后端
非优秀程序员2 小时前
第08章 马云的反击|《互联网产品恩仇录》
后端
非优秀程序员2 小时前
题记|《互联网产品恩仇录》
后端·程序员·创业
非优秀程序员2 小时前
第04章 邮箱大战,163 与 QQ 的暗战|《互联网产品恩仇录》
java·vue.js·架构
知已难寻3 小时前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
文丑颜不良啊5 小时前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang6 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
Gopher_HBo6 小时前
Go CPU 剖析的"地基":信号、CAS、写屏障、无锁环形缓冲
后端
panbook6 小时前
从0到1实现一个分布式时序数据库(二):写入成功之后,如何用 WAL 找回数据
后端