Spring 配置 Bean 全指南:从 XML 到注解的完整实践
Spring 配置 Bean 的核心目标是将 Bean 的创建、依赖管理交给 IOC 容器,通过 XML 或注解定义 Bean 的元数据(如类名、依赖、生命周期),最终由容器统一实例化并装配。从 “XML 配置” 到 “注解配置”,系统拆解 Bean 的配置方式、依赖注入逻辑、自动装配规则及歧义性处理,同时对比两种配置方式的优缺点与最佳实践。
XML 配置 Bean:传统且灵活的配置方式
XML 是 Spring 早期最主流的 Bean 配置方式,支持复杂的 Bean 定义(如工厂 Bean、集合属性),适合需要明确配置流程的场景。其核心是通过 <bean> 标签定义 Bean,配合 <property>/<constructor-arg> 注入依赖。
1. 基础 Bean 配置:构造器与 Setter 注入
Spring 实例化 Bean 主要依赖构造器(默认无参构造器),依赖注入(DI)分为 Setter 注入(属性注入)和 构造器注入,分别对应不同的使用场景。
(1)Setter 注入(最常用)
核心原理:Spring 先通过无参构造器实例化 Bean,再调用属性的 setter 方法注入依赖。
关键要求:Bean 类必须提供无参构造器(若未显式定义任何构造器,JVM 会默认生成;若显式定义有参构造器,需手动添加无参构造器)。
1.1 简单类型注入(value 属性)
注入基本类型(String、int、double 等)或字符串,使用 value 属性:
1 | <!-- 配置 HelloWorld Bean,Setter 注入 name 属性 --> |
对应的 Java 类:
1 | public class HelloWorld { |
1.2 引用类型注入(ref 属性)
注入其他 Bean(如 Car 注入到 Person),使用 ref 属性引用目标 Bean 的 id:
1 | <!-- 1. 配置 Car Bean(构造器注入,后续详解) --> |
对应的 Java 类:
1 | public class Person { |
1.3 特殊值注入(null、特殊字符)
- 注入 null 值:使用
<null/>标签(直接写value="null"会注入字符串 “null”); - 注入特殊字符(如
<、>、&):使用 XML 转义字符(如<代表<)或<![CDATA[]]>包裹。
示例:
1 | <bean id="helloWorld" class="com.zhanghe.study.spring4.beans.helloworld.HelloWorld"> |
(2)构造器注入(强制依赖)
核心原理:Spring 直接调用有参构造器实例化 Bean,同时注入依赖,适合强制依赖(如 Car 的品牌和价格必须指定)。
配置方式:通过 <constructor-arg> 标签指定构造器参数,支持 index(参数位置)、type(参数类型)、name(参数名)匹配。
示例 1:按位置(index)和类型(type)匹配
1 | <bean id="car" class="com.zhanghe.study.spring4.beans.beantest.Car"> |
对应的 Car 类(有参构造器):
1 | public class Car { |
示例 2:按参数名(name)匹配
若构造器参数名明确,可直接用 name 匹配,更易维护(需确保编译时保留参数名,或通过 @ConstructorProperties 指定):
1 | <bean id="car" class="com.zhanghe.study.spring4.beans.beantest.Car"> |
2. 复杂属性配置:集合与工具类
对于集合类型(List、Set、Map、Properties),Spring 提供专用标签配置,支持注入简单值或引用其他 Bean。
(1)List/Set 配置
<list>:有序集合,允许重复元素;<set>:无序集合,不允许重复元素;- 注入引用 Bean 用
<ref bean="beanId"/>,注入简单值用<value>。
示例:给 Person 注入List<Car>和 Set<String>:
1 | <bean id="car1" class="com.zhanghe.study.spring4.beans.beantest.Car"> |
对应的 Person 类:
1 | public class Person { |
(2)Map 配置
<map> 标签通过 <entry> 配置键值对,支持:
key/value:简单类型键值;key-ref/value-ref:引用 Bean 作为键 / 值。
示例:注入 Map
1 | <bean id="person" class="com.zhanghe.study.spring4.beans.beantest.Person"> |
(3)Properties 配置
<props> 标签配置键值对(仅支持 String 类型),常用于配置文件参数(如数据库连接信息):
1 | <bean id="dataSource" class="com.example.DataSource"> |
(4)复用集合:<util: 标签
若多个 Bean 需共用同一集合,可通过 <util: 标签定义 “集合 Bean”,再通过 ref 引用,避免重复配置。
注意:需引入 util 命名空间。
示例:
1 | <!-- 1. 引入 util 命名空间(头部配置) --> |
3. 特殊 Bean 配置:工厂模式与 FactoryBean
对于实例化逻辑复杂的 Bean(如需多步初始化、依赖外部资源),Spring 支持通过工厂模式或 FactoryBean 配置,将实例化逻辑封装到工厂类中。
(1)FactoryBean:自定义工厂 Bean
核心区别:FactoryBean 是 “生产 Bean 的 Bean”(本身是 Bean,容器会调用其 getObject() 生成目标 Bean);BeanFactory 是 “Bean 的工厂”(容器顶层接口)。
适用场景:实例化逻辑复杂(如 MyBatis 的 SqlSessionFactoryBean、Spring 的 DataSourceFactoryBean)。
实现步骤:
- 实现
FactoryBean<T>接口,泛型T为目标 Bean 类型; - 重写
getObject()(生成目标 Bean)、getObjectType()(目标 Bean 类型)、isSingleton()(是否单例); - 在 XML 中配置 FactoryBean,容器会自动调用
getObject()生成目标 Bean。
示例:自定义 CarFactoryBean 生成 Car:
1 | // 1. 实现 FactoryBean<Car> |
1 | <!-- 2. 配置 FactoryBean(容器会自动调用 getObject() 生成 Car) --> |
获取 FactoryBean 本身:若需获取 FactoryBean 实例,需在 id 前加 & 前缀:
1 | // 获取 CarFactoryBean 本身(非目标 Car) |
(2)静态工厂:通过静态方法生成 Bean
核心原理:调用工厂类的静态方法生成 Bean,XML 中通过 class 指定静态工厂类,factory-method 指定静态方法。
适用场景:目标 Bean 的实例化逻辑封装在静态方法中(如工具类生成单例对象)。
示例:
1 | // 1. 静态工厂类(含静态方法 getCar()) |
1 | <!-- 2. 配置静态工厂生成 Bean --> |
(3)实例工厂:通过实例方法生成 Bean
核心原理:先实例化工厂类(生成工厂 Bean),再调用工厂实例的非静态方法生成目标 Bean,XML 中通过 factory-bean 指定工厂实例,factory-method 指定实例方法。
区别于静态工厂:需先创建工厂实例,再调用方法(静态工厂无需实例化)。
示例:
1 | // 1. 实例工厂类(含非静态方法 getCar()) |
1 | <!-- 2. 1 配置工厂实例(先创建 InstanceCarFactory Bean) --> |
4. XML 自动装配:减少手动注入
手动配置 <property>/<constructor-arg> 繁琐,Spring 支持 自动装配(AutoWiring),通过 autowire 属性让容器自动匹配依赖。
自动装配属性(autowire)
| autowire 值 | 匹配规则 | 适用场景 |
|---|---|---|
byName |
按 “属性名” 匹配 Bean 的 id(如 Person 的 car 属性 → 匹配 id="car" 的 Bean) |
属性名与 Bean id 一致的场景 |
byType |
按 “属性类型” 匹配 Bean(如 Person 的 Car 类型属性 → 匹配所有 Car 类型 Bean) |
某类型仅一个 Bean 的场景 |
constructor |
按 “构造器参数类型” 匹配(类似 byType,但用于构造器注入) | 构造器依赖需自动匹配的场景 |
no |
不自动装配(默认值) | 手动注入的场景 |
示例:byName 自动装配(Person 的 car 属性匹配 id="car" 的 Bean):
1 | <!-- 1. 配置 Car Bean(id="car",与 Person 的 car 属性名一致) --> |
自动装配的歧义性问题
当 autowire="byType" 时,若同一类型存在多个 Bean(如 2 个 Car 类型 Bean),容器无法确定注入哪个,会抛出 NoUniqueBeanDefinitionException。
解决方案:
XML 中设置
primary="true":标记某 Bean 为 “首选 Bean”,容器优先选择;1
2
3
4
5
6
7<!-- 标记 car1 为首选 Bean,byType 时优先注入 -->
<bean id="car1" class="com.zhanghe.study.spring4.beans.beantest.Car" primary="true">
<constructor-arg name="brand" value="法拉利"/>
</bean>
<bean id="car2" class="com.zhanghe.study.spring4.beans.beantest.Car">
<constructor-arg name="brand" value="玛莎拉蒂"/>
</bean>排除多余 Bean:通过
<bean autowire-candidate="false"/>标记某 Bean 不参与自动装配;1
2
3<!-- car2 不参与自动装配,byType 时仅匹配 car1 -->
<bean id="car2" class="com.zhanghe.study.spring4.beans.beantest.Car" autowire-candidate="false">
</bean>
注解配置 Bean:简洁高效的现代方式
随着 Spring 发展,注解配置逐渐取代 XML,核心是通过 组件扫描(@ComponentScan)自动发现 Bean,配合 自动装配注解(@Autowired/@Resource)注入依赖,大幅简化配置。
1. 组件扫描:自动发现 Bean
Spring 能扫描指定包下的 “组件类”(标注 @Component 及其衍生注解的类),自动将其注册为 Bean,无需手动配置 <bean>。
(1)核心组件注解
| 注解 | 含义 | 适用层 |
|---|---|---|
@Component |
通用组件注解(基础注解) | 任意层 |
@Repository |
持久层组件(DAO 层),额外支持异常转换(如将 JDBC 异常转为 Spring 异常) | DAO 层(如 UserMapperImpl) |
@Service |
业务层组件(Service 层) | Service 层(如 UserService) |
@Controller |
表现层组件(Web 层),支持 Spring MVC 请求映射 | Controller 层(如 UserController) |
Bean 命名规则:
- 默认:类名首字母小写(如
UserService→ Bean id 为userService); - 自定义:通过注解的
value属性指定(如@Service("userServiceV2")→ Bean id 为userServiceV2)。
(2)开启组件扫描
需通过配置类或 XML 开启组件扫描,指定扫描的包路径(Spring 会递归扫描子包)。
方式 1:XML 开启(兼容传统项目)
1 | <!-- 1. 引入 context 命名空间(头部配置) --> |
方式 2:Java 配置类开启(现代项目推荐)
通过 @Configuration(标记配置类)和 @ComponentScan(指定扫描包)替代 XML:
1 | // 配置类(替代 XML) |
使用配置类创建容器:
1 | // 从配置类加载容器(无需 XML) |
(3)自定义组件扫描规则
@ComponentScan 支持过滤规则,仅扫描或排除特定组件:
- 排除过滤(excludeFilters):排除不需要扫描的组件;
- 包含过滤(includeFilters):仅扫描指定组件(需关闭默认过滤
useDefaultFilters=false)。
示例:仅扫描 @Service 注解的组件,排除 @Controller:
1 |
|
过滤类型(FilterType):
ANNOTATION:按注解过滤(如@Service);ASSIGNABLE_TYPE:按类类型过滤(如UserService.class);ASPECTJ:按 AspectJ 表达式过滤;REGEX:按类名正则表达式过滤。
2. 注解自动装配:注入依赖
组件扫描仅解决 “Bean 的注册”,依赖注入需通过自动装配注解实现。Spring 支持 4 种核心注解,各有差异。
(1)@Autowired:Spring 原生自动装配
核心规则:
- 默认按 byType 匹配(根据属性类型查找 Bean);
- 若同一类型存在多个 Bean,自动转为 byName(按属性名匹配 Bean id);
- 默认要求依赖必须存在(若允许不存在,需设置
required=false)。
示例 1:字段注入(最简洁)
直接在字段上标注 @Autowired,无需 setter 方法:
1 | // 标记为 Service 组件,自动注册为 Bean |
示例 2:setter 注入(适合可选依赖)
在 setter 方法上标注 @Autowired,支持 required=false(允许依赖不存在):
1 |
|
示例 3:构造器注入(推荐,强制依赖)
在构造器上标注 @Autowired(Spring 4.3+ 后,若仅有一个有参构造器,可省略 @Autowired):
1 |
|
(2)@Qualifier:解决歧义性
当 @Autowired 按类型匹配到多个 Bean 时,需用 @Qualifier 按 Bean id 明确指定注入哪个,与 @Autowired 配合使用。
示例:
1 | // 1. 两个 UserDao 实现类(均被扫描注册) |
(3)@Resource:JSR-250 标准注解
@Resource 是 Java EE 标准注解(非 Spring 原生),支持按 byName 或 byType 匹配,优先级:
- 若指定
name属性,按name(Bean id)匹配; - 若未指定
name,先按属性名(byName)匹配,失败后按类型(byType)匹配; - 不支持
required=false(依赖必须存在)。
示例:
1 |
|
(4)@Inject:JSR-330 标准注解
@Inject 是 JSR-330 标准注解(需导入 javax.inject:javax.inject:1 依赖),功能与 @Autowired 类似:
- 默认按 byType 匹配;
- 需配合
@Named(类似@Qualifier)解决歧义; - 不支持
required=false(依赖必须存在)。
示例:
1 | // 需导入依赖:javax.inject:javax.inject:1 |
3. 注解配置的歧义性处理
除了 @Qualifier/@Named,还可通过 @Primary 标记 “首选 Bean”,解决 @Autowired 按类型匹配的歧义。
示例:
1 | // 1. @Primary 标记首选 Bean(容器优先选择) |
XML 与注解配置的对比与最佳实践
1. 两种配置方式的对比
| 维度 | XML 配置 | 注解配置 |
|---|---|---|
| 配置复杂度 | 繁琐(需手动写标签) | 简洁(注解 + 组件扫描) |
| 可读性 | 集中管理,适合复杂配置 | 分散在类中,适合简单配置 |
| 灵活性 | 支持动态修改(无需重新编译) | 静态配置(修改需重新编译) |
| 适用场景 | 传统项目、复杂配置(如工厂 Bean) | 现代项目、简洁开发(Spring Boot) |
| 学习成本 | 需记忆标签规则(如 <constructor-arg>) |
需记忆注解含义(如 @Autowired) |
2. 最佳实践
现代项目优先用注解 + JavaConfig:Spring Boot 推荐使用
@Configuration+@ComponentScan+@Autowired,配合自动配置(@EnableAutoConfiguration),几乎无需 XML;复杂 Bean 用
@Bean手动注册:如数据源(DataSource)、连接池等,通过@Bean在配置类中手动注册,逻辑更清晰:1
2
3
4
5
6
7
8
9
10
11
public class DataSourceConfig {
// 手动注册 DataSource Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}XML 与注解混合使用:传统项目可保留核心 XML 配置,同时通过
<context:component-scan>开启注解扫描,逐步迁移;依赖注入优先用构造器注入:构造器注入确保依赖在 Bean 初始化时已就绪(
final字段),避免空指针,且支持单元测试 mock 依赖。
总结
Spring 配置 Bean 的核心是通过元数据(XML / 注解)告诉容器 “如何创建 Bean 及注入依赖”,两种配置方式各有优劣:
- XML 适合复杂、需动态修改的配置,是 Spring 早期的主流方式;
- 注解适合简洁、现代的开发,配合 JavaConfig 成为 Spring Boot 及微服务项目的首选