人人都会AI编程

8.1 Bean 的三种配置方式:XML 配置、JavaConfig、注解驱动

更新时间:2026-07-10

在 Spring 容器中,Bean 的定义与装配方式直接决定了项目的可维护性、可读性和团队协作效率。从早期经典的 XML 配置,到后来的注解驱动,再到如今主流的 JavaConfig,Spring 为开发者提供了灵活多变的装配策略。理解这三种方式的各自特点和适用场景,是驾驭 Spring 容器的基本功。

8.1.1 XML 配置:经典的结构化声明

早期的 Spring 应用,几乎完全依赖 XML 来描述 Bean 以及它们之间的依赖关系。虽然现在不再是主流,但仍有大量遗留系统在使用,并且某些场景(如解耦外部集成)依然实用。

一个典型的 XML 配置如下(假设存在 applicationContext.xml):

<?xml version="1.0" encoding="UTF-8"?>
<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
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义一个数据源 -->
    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb"/>
        <property name="username" value="root"/>
        <property name="password" value="secret"/>
    </bean>

    <!-- 定义一个 DAO,并注入数据源 -->
    <bean id="userDao" class="com.example.dao.UserDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 定义一个 Service,构造器注入 DAO -->
    <bean id="userService" class="com.example.service.UserService">
        <constructor-arg ref="userDao"/>
    </bean>
</beans>

然后在代码中通过 ClassPathXmlApplicationContext 加载这个配置文件:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);

优点

  • 完全解耦 Java 代码:Bean 的定义和装配完全集中在配置文件中,业务类完全是纯 POJO,不带任何 Spring 注解。
  • 集中管理:所有 Bean 的关系一目了然,适合大规模系统做全局概览。
  • 无侵入性:切换不同的 Spring 版本或框架时,核心业务类不受影响。

缺点

  • 编写繁琐:每个 Bean 及其属性、依赖都必须手动配置,项目变大后 XML 文件会迅速膨胀,难以维护。
  • 缺乏编译期检查:类名、属性名都是字符串,拼写错误要到容器启动时才能发现。
  • 重构困难:重命名类或包时,必须同步修改 XML 文件,容易遗漏。

适用场景:遗留系统维护、需要高可配置性且与 Java 代码完全解耦的模块、基础框架的底层配置(如 AOP 全局配置)。

8.1.2 注解驱动:内聚的声明式装配

为了解决 XML 的臃肿问题,Spring 从 2.5 版本起引入了注解驱动的 Bean 配置。通过 @Component@Autowired 等标准注解,把配置信息放到了 Java 类本身上。

开启组件扫描(XML 方式或 JavaConfig 方式):

<!-- XML 中开启扫描 -->
<context:component-scan base-package="com.example"/>

或者用 JavaConfig:

@Configuration
@ComponentScan("com.example")
public class AppConfig {
}

标记 Bean 并注入依赖

@Repository
public class UserDao {
    // ...
}

@Service
public class UserService {
    private final UserDao userDao;

    @Autowired  // 构造器注入(Spring 4.3+ 可省略 @Autowired)
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

为了让 Spring 识别这些注解,必须显式启用注解驱动支持(在 XML 中通过 <context:annotation-config/> 或组件扫描自动激活)。Spring Boot 中自动配置已经替我们完成了这些操作。

常用的原型注解@Component 的语义特化):

  • @Component:通用组件
  • @Service:服务层
  • @Repository:数据访问层(并自动提供异常转译)
  • @Controller / @RestController:Web 控制器

注入注解

  • @Autowired:按类型注入(可配合 @Qualifier 按名称精确匹配)
  • @Resource(JSR-250):默认按名称,再按类型
  • @Inject(JSR-330):与 @Autowired 类似,但功能稍弱

优点

  • 代码简洁:Bean 的定义与业务类本身放在一起,减少了配置文件。
  • 重构友好:类名、包名变更后,依赖关系仍然由注解自动维护(IDE 可安全重构)。
  • 开发体验好:使用注解驱动后,大部分常规 Bean 不再需要显式声明,Spring 自动发现并装配。

缺点

  • 侵入性:业务类带上了 Spring 注解,技术上不再是纯 POJO(虽然影响很小)。
  • 分散配置:大型项目中,依赖关系散落在各个类中,不如集中配置文件直观。
  • 隐式依赖@Autowired 自动按类型匹配,如果存在多个同类型 Bean,容易引发启动异常,需要借助 @Primary@Qualifier 解决。

适用场景:绝大多数现代 Spring 应用,特别是 Web 服务、微服务,注解驱动是主力配置手段。

8.1.3 JavaConfig:显式、类型安全的装配

JavaConfig 是 Spring 3.0 引入的纯 Java 配置方式,它使用 @Configuration@Bean 注解来定义 Bean,完全摒弃 XML,同时保留集中配置的优点。

示例

@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        config.setUsername("root");
        config.setPassword("secret");
        return new HikariDataSource(config);
    }

    @Bean
    public UserDao userDao(DataSource dataSource) {  // 参数自动注入
        return new UserDao(dataSource);
    }

    @Bean
    public UserService userService(UserDao userDao) {
        return new UserService(userDao);
    }
}

加载方式:

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);

关键点

  • @Configuration 标记类为配置类,相当于一个增强的 @Component,内部可定义多个 @Bean 方法。
  • @Bean 方法返回的对象会被注册为 Spring 容器中的 Bean,方法名默认就是 Bean 名称。
  • 方法参数会被容器自动注入其他 Bean(按类型匹配),无需显式 @Autowired
  • JavaConfig 类本身也是容器管理的 Bean(可被注入),所以可以依赖其他配置。

与注解驱动的关系:JavaConfig 常和注解驱动组合使用。通常用 @ComponentScan 启用自动发现,同时用 @Bean 手动定义那些无法直接加注解的 Bean(例如第三方库的对象、需要复杂初始化的组件)。Spring Boot 的自动配置底层正是无数的 @Configuration 类。

优点

  • 集中可见:将一组相关的 Bean 定义集中在一个配置类中,便于理解和维护(例如 DataSourceConfigSecurityConfig)。
  • 类型安全:一切都是 Java 代码,拼写错误、类型错误都在编译期暴露。
  • 灵活:可以在 @Bean 方法内部编写任意初始化逻辑、条件判断,甚至调用其他方法。
  • 可测试:配置类本身也可以作为单元测试的对象。

缺点

  • 相对注解驱动,需要写更多的显式声明代码。
  • 若配置类过多,仍然会面临一定的管理复杂度。

适用场景

  • 定义第三方库的 Bean(如 RestTemplateDataSourceRedisConnectionFactory
  • 需要复杂初始化逻辑的对象
  • 同一接口多个实现需要显式命名的场合
  • 框架级基础设施的配置(安全、事务、调度)

8.1.4 三种方式的混合与演变

Spring 并不强制开发者只选择一种方式,三种方式可以在同一个项目中混合使用。典型的组合是:

  • 注解驱动负责业务 Bean 的自动扫描(@Service@Repository 等)
  • JavaConfig负责基础设施 Bean 和第三方集成的显式声明
  • XML(若有遗留)通过 <import resource="classpath:old-config.xml"/>@ImportResource 并入

在 Spring Boot 项目中,几乎看不到 XML 配置的身影,主流模式是 “自动配置 + 少量 JavaConfig + 注解驱动”。但理解 XML 仍然重要,因为很多老的 Spring 项目、企业集成文档以及 Spring 内部某些命名空间的配置(如 Spring Security 早期版本)依然基于 XML 示例。

从演变路径来看:XML → 注解 + XML → JavaConfig + 注解 → 习惯优于配置(Boot 自动配置)。每一种新方式的出现,都在降低开发者的配置成本,同时让配置更加内聚和类型安全。掌握这三种方式,你就能读懂任何时期的 Spring 项目,并根据实际场景做出最合适的选择。