0%

spring配置bean

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
2
3
4
5
6
<!-- 配置 HelloWorld Bean,Setter 注入 name 属性 -->
<bean id="helloWorld" class="com.zhanghe.study.spring4.beans.helloworld.HelloWorld">
<!-- name:对应 setName() 方法(去掉 set 后首字母小写,与成员变量名无关) -->
<!-- value:注入简单类型值 -->
<property name="name" value="Spring Hello"/>
</bean>

对应的 Java 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HelloWorld {
private String name;

// 必须提供无参构造器(Setter 注入依赖)
public HelloWorld() {}

// Setter 方法(Spring 调用此方法注入值)
public void setName(String name) {
this.name = name;
}

public void sayHello() {
System.out.println("Hello, " + name);
}
}
1.2 引用类型注入(ref 属性)

注入其他 Bean(如 Car 注入到 Person),使用 ref 属性引用目标 Bean 的 id

1
2
3
4
5
6
7
8
9
10
11
<!-- 1. 配置 Car Bean(构造器注入,后续详解) -->
<bean id="car" class="com.zhanghe.study.spring4.beans.beantest.Car">
<constructor-arg value="法拉利" index="0"/>
<constructor-arg value="20000.0" type="double"/>
</bean>

<!-- 2. 配置 Person Bean,Setter 注入 Car 引用 -->
<bean id="person" class="com.zhanghe.study.spring4.beans.beantest.Person">
<property name="name" value="张三"/> <!-- 简单类型 -->
<property name="car" ref="car"/> <!-- 引用类型:ref 指向 Car 的 id -->
</bean>

对应的 Java 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Person {
private String name;
private Car car; // 引用类型

// 无参构造器(必须)
public Person() {}

// Setter 方法
public void setName(String name) { this.name = name; }
public void setCar(Car car) { this.car = car; }

// 省略 toString()
}
1.3 特殊值注入(null、特殊字符)
  • 注入 null 值:使用 <null/> 标签(直接写 value="null" 会注入字符串 “null”);
  • 注入特殊字符(如 <>&):使用 XML 转义字符(如 < 代表 <)或 <![CDATA[]]> 包裹。

示例:

1
2
3
4
5
6
7
8
9
10
<bean id="helloWorld" class="com.zhanghe.study.spring4.beans.helloworld.HelloWorld">
<!-- 注入 null 值 -->
<property name="name">
<null/>
</property>
<!-- 注入特殊字符:<年龄>18</年龄> -->
<property name="desc">
<value><![CDATA[<年龄>18</年龄>]]></value>
</property>
</bean>
(2)构造器注入(强制依赖)

核心原理:Spring 直接调用有参构造器实例化 Bean,同时注入依赖,适合强制依赖(如 Car 的品牌和价格必须指定)。
配置方式:通过 <constructor-arg> 标签指定构造器参数,支持 index(参数位置)、type(参数类型)、name(参数名)匹配。

示例 1:按位置(index)和类型(type)匹配
1
2
3
4
5
6
<bean id="car" class="com.zhanghe.study.spring4.beans.beantest.Car">
<!-- index=0:对应构造器第一个参数(品牌,String 类型) -->
<constructor-arg value="法拉利" index="0"/>
<!-- type="double":明确参数类型(避免与 int 混淆),对应第二个参数(价格) -->
<constructor-arg value="20000.0" type="double"/>
</bean>

对应的 Car 类(有参构造器):

1
2
3
4
5
6
7
8
9
10
11
12
public class Car {
private String brand; // 品牌(构造器第一个参数)
private double price; // 价格(构造器第二个参数)

// 有参构造器(无参构造器可选,因使用构造器注入)
public Car(String brand, double price) {
this.brand = brand;
this.price = price;
}

// 省略 getter/toString()
}
示例 2:按参数名(name)匹配

若构造器参数名明确,可直接用 name 匹配,更易维护(需确保编译时保留参数名,或通过 @ConstructorProperties 指定):

1
2
3
4
<bean id="car" class="com.zhanghe.study.spring4.beans.beantest.Car">
<constructor-arg name="brand" value="法拉利"/> <!-- 参数名=brand -->
<constructor-arg name="price" value="20000.0"/> <!-- 参数名=price -->
</bean>

2. 复杂属性配置:集合与工具类

对于集合类型(List、Set、Map、Properties),Spring 提供专用标签配置,支持注入简单值或引用其他 Bean。

(1)List/Set 配置
  • <list>:有序集合,允许重复元素;
  • <set>:无序集合,不允许重复元素;
  • 注入引用 Bean 用 <ref bean="beanId"/>,注入简单值用 <value>

示例:给 Person 注入List<Car>Set<String>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<bean id="car1" class="com.zhanghe.study.spring4.beans.beantest.Car">
<constructor-arg name="brand" value="法拉利"/>
<constructor-arg name="price" value="20000.0"/>
</bean>

<bean id="car2" class="com.zhanghe.study.spring4.beans.beantest.Car">
<constructor-arg name="brand" value="玛莎拉蒂"/>
<constructor-arg name="price" value="30000.0"/>
</bean>

<bean id="person" class="com.zhanghe.study.spring4.beans.beantest.Person">
<property name="name" value="张三"/>
<!-- 注入 List<Car>:有序,可重复 -->
<property name="carList">
<list>
<ref bean="car1"/> <!-- 引用 Car Bean -->
<ref bean="car2"/>
<ref bean="car1"/> <!-- 允许重复 -->
</list>
</property>
<!-- 注入 Set<String>:无序,去重 -->
<property name="hobbies">
<set>
<value>篮球</value> <!-- 简单值 -->
<value>足球</value>
<value>篮球</value> <!-- 重复值会被去重 -->
</set>
</property>
</bean>

对应的 Person 类:

1
2
3
4
5
6
7
public class Person {
private String name;
private List<Car> carList; // List 集合
private Set<String> hobbies; // Set 集合

// 无参构造器 + Setter 方法
}
(2)Map 配置

<map> 标签通过 <entry> 配置键值对,支持:

  • key/value:简单类型键值;
  • key-ref/value-ref:引用 Bean 作为键 / 值。

示例:注入 Map

1
2
3
4
5
6
7
8
9
10
<bean id="person" class="com.zhanghe.study.spring4.beans.beantest.Person">
<!-- 注入 Map<String, Car> -->
<property name="carMap">
<map>
<!-- key=字符串,value-ref=Car Bean -->
<entry key="日常代步" value-ref="car1"/>
<entry key="商务接待" value-ref="car2"/>
</map>
</property>
</bean>
(3)Properties 配置

<props> 标签配置键值对(仅支持 String 类型),常用于配置文件参数(如数据库连接信息):

1
2
3
4
5
6
7
8
9
10
<bean id="dataSource" class="com.example.DataSource">
<!-- 注入 Properties(数据库配置) -->
<property name="dbConfig">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
<prop key="url">jdbc:mysql://localhost:3306/test</prop>
</props>
</property>
</bean>
(4)复用集合:<util: 标签

若多个 Bean 需共用同一集合,可通过 <util: 标签定义 “集合 Bean”,再通过 ref 引用,避免重复配置。
注意:需引入 util 命名空间。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- 1. 引入 util 命名空间(头部配置) -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util" <!-- 新增 util 命名空间 -->
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util <!-- 新增 schema 地址 -->
http://www.springframework.org/schema/util/spring-util.xsd">

<!-- 2. 定义可复用的 List<Car> 集合 Bean -->
<util:list id="commonCarList">
<ref bean="car1"/>
<ref bean="car2"/>
</util:list>

<!-- 3. 多个 Bean 引用同一集合 -->
<bean id="person1" class="com.zhanghe.study.spring4.beans.beantest.Person">
<property name="carList" ref="commonCarList"/> <!-- 引用集合 Bean -->
</bean>

<bean id="person2" class="com.zhanghe.study.spring4.beans.beantest.Person">
<property name="carList" ref="commonCarList"/> <!-- 复用集合 -->
</bean>
</beans>

3. 特殊 Bean 配置:工厂模式与 FactoryBean

对于实例化逻辑复杂的 Bean(如需多步初始化、依赖外部资源),Spring 支持通过工厂模式FactoryBean 配置,将实例化逻辑封装到工厂类中。

(1)FactoryBean:自定义工厂 Bean

核心区别FactoryBean 是 “生产 Bean 的 Bean”(本身是 Bean,容器会调用其 getObject() 生成目标 Bean);BeanFactory 是 “Bean 的工厂”(容器顶层接口)。
适用场景:实例化逻辑复杂(如 MyBatis 的 SqlSessionFactoryBean、Spring 的 DataSourceFactoryBean)。

实现步骤:
  1. 实现 FactoryBean<T> 接口,泛型 T 为目标 Bean 类型;
  2. 重写 getObject()(生成目标 Bean)、getObjectType()(目标 Bean 类型)、isSingleton()(是否单例);
  3. 在 XML 中配置 FactoryBean,容器会自动调用 getObject() 生成目标 Bean。

示例:自定义 CarFactoryBean 生成 Car:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1. 实现 FactoryBean<Car>
public class CarFactoryBean implements FactoryBean<Car> {
// 生成目标 Bean(核心方法)
@Override
public Car getObject() throws Exception {
// 复杂实例化逻辑:如从配置文件读取参数、调用外部接口等
return new Car("玛莎拉蒂", 30000.0);
}

// 目标 Bean 的类型
@Override
public Class<?> getObjectType() {
return Car.class;
}

// 是否单例(默认 true,容器会缓存目标 Bean)
@Override
public boolean isSingleton() {
return true;
}
}
1
2
3
4
5
6
<!-- 2. 配置 FactoryBean(容器会自动调用 getObject() 生成 Car) -->
<bean id="car" class="com.zhanghe.study.spring4.beans.beantest.CarFactoryBean"/>

<!-- 3. 获取目标 Bean(直接通过 id="car" 获取,无需关心 FactoryBean) -->
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Car car = context.getBean("car", Car.class); // 获取的是 Car 对象,而非 CarFactoryBean

获取 FactoryBean 本身:若需获取 FactoryBean 实例,需在 id 前加 & 前缀:

1
2
// 获取 CarFactoryBean 本身(非目标 Car)
CarFactoryBean factoryBean = context.getBean("&car", CarFactoryBean.class);
(2)静态工厂:通过静态方法生成 Bean

核心原理:调用工厂类的静态方法生成 Bean,XML 中通过 class 指定静态工厂类,factory-method 指定静态方法。
适用场景:目标 Bean 的实例化逻辑封装在静态方法中(如工具类生成单例对象)。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. 静态工厂类(含静态方法 getCar())
public class StaticCarFactory {
// 静态缓存(模拟从配置加载)
private static Map<String, Car> carMap = new HashMap<>();

// 静态初始化(类加载时执行)
static {
carMap.put("法拉利", new Car("法拉利", 20000.0));
carMap.put("玛莎拉蒂", new Car("玛莎拉蒂", 30000.0));
}

// 静态工厂方法(根据名称获取 Car)
public static Car getCar(String brand) {
return carMap.get(brand);
}
}
1
2
3
4
5
6
7
<!-- 2. 配置静态工厂生成 Bean -->
<bean id="car"
class="com.zhanghe.study.spring4.beans.beantest.StaticCarFactory" <!-- 静态工厂类 -->
factory-method="getCar"> <!-- 静态方法名 -->
<!-- 给静态方法传参(brand="法拉利") -->
<constructor-arg name="brand" value="法拉利"/>
</bean>
(3)实例工厂:通过实例方法生成 Bean

核心原理:先实例化工厂类(生成工厂 Bean),再调用工厂实例的非静态方法生成目标 Bean,XML 中通过 factory-bean 指定工厂实例,factory-method 指定实例方法。
区别于静态工厂:需先创建工厂实例,再调用方法(静态工厂无需实例化)。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. 实例工厂类(含非静态方法 getCar())
public class InstanceCarFactory {
private Map<String, Car> carMap;

// 非静态初始化(工厂实例化时执行)
public InstanceCarFactory() {
carMap = new HashMap<>();
carMap.put("法拉利", new Car("法拉利", 20000.0));
carMap.put("玛莎拉蒂", new Car("玛莎拉蒂", 30000.0));
}

// 实例工厂方法(非静态,需工厂实例调用)
public Car getCar(String brand) {
return carMap.get(brand);
}
}
1
2
3
4
5
6
7
8
9
10
11
<!-- 2. 1 配置工厂实例(先创建 InstanceCarFactory Bean) -->
<bean id="instanceCarFactory"
class="com.zhanghe.study.spring4.beans.beantest.InstanceCarFactory"/>

<!-- 2.2 配置实例工厂生成目标 Bean -->
<bean id="car"
factory-bean="instanceCarFactory" <!-- 工厂实例的 id -->
factory-method="getCar"> <!-- 实例方法名 -->
<!-- 给实例方法传参 -->
<constructor-arg name="brand" value="玛莎拉蒂"/>
</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
2
3
4
5
6
7
8
9
10
11
12
<!-- 1. 配置 Car Bean(id="car",与 Person 的 car 属性名一致) -->
<bean id="car" class="com.zhanghe.study.spring4.beans.beantest.Car">
<constructor-arg name="brand" value="法拉利"/>
</bean>

<!-- 2. 配置 Person Bean,autowire="byName" 自动装配 car 属性 -->
<bean id="person"
class="com.zhanghe.study.spring4.beans.beantest.Person"
autowire="byName">
<!-- 仅需手动配置 name 属性,car 属性由容器自动匹配 id="car" 的 Bean -->
<property name="name" value="张三"/>
</bean>
自动装配的歧义性问题

autowire="byType" 时,若同一类型存在多个 Bean(如 2 个 Car 类型 Bean),容器无法确定注入哪个,会抛出 NoUniqueBeanDefinitionException

解决方案

  1. 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>
  2. 排除多余 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
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 1. 引入 context 命名空间(头部配置) -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 2. 开启组件扫描,扫描 com.zhanghe.study.spring4.beans.annotationtest 包 -->
<context:component-scan base-package="com.zhanghe.study.spring4.beans.annotationtest"/>
</beans>
方式 2:Java 配置类开启(现代项目推荐)

通过 @Configuration(标记配置类)和 @ComponentScan(指定扫描包)替代 XML:

1
2
3
4
5
6
// 配置类(替代 XML)
@Configuration
@ComponentScan(basePackages = "com.zhanghe.study.spring4.beans.annotationtest")
public class AppConfig {
// 可在此类中通过 @Bean 手动注册复杂 Bean(如 DataSource)
}

使用配置类创建容器

1
2
3
4
// 从配置类加载容器(无需 XML)
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取自动扫描的 Bean(id 默认为 userService)
UserService userService = context.getBean("userService", UserService.class);
(3)自定义组件扫描规则

@ComponentScan 支持过滤规则,仅扫描或排除特定组件:

  • 排除过滤(excludeFilters):排除不需要扫描的组件;
  • 包含过滤(includeFilters):仅扫描指定组件(需关闭默认过滤 useDefaultFilters=false)。

示例:仅扫描 @Service 注解的组件,排除 @Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@ComponentScan(
basePackages = "com.zhanghe.study.spring4.beans.annotationtest",
useDefaultFilters = false, // 关闭默认过滤(默认扫描所有组件注解)
includeFilters = { // 仅扫描 @Service 注解的组件
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Service.class)
},
excludeFilters = { // 排除 @Controller 注解的组件
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)
}
)
public class AppConfig {
}

过滤类型(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service // 标记为 Service 组件,自动注册为 Bean
public class UserService {
// 自动装配 UserDao(byType 匹配 UserDao 类型 Bean)
@Autowired
private UserDao userDao;

// 业务方法(使用 userDao)
public User getUserById(Long id) {
return userDao.selectById(id);
}
}

// UserDao 组件(被扫描注册)
@Repository
public class UserDaoImpl implements UserDao {
@Override
public User selectById(Long id) {
// 模拟数据库查询
return new User(id, "张三");
}
}
示例 2:setter 注入(适合可选依赖)

setter 方法上标注 @Autowired,支持 required=false(允许依赖不存在):

1
2
3
4
5
6
7
8
9
10
@Service
public class UserService {
private UserDao userDao;

// setter 注入,允许 userDao 为 null
@Autowired(required = false)
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
示例 3:构造器注入(推荐,强制依赖)

在构造器上标注 @Autowired(Spring 4.3+ 后,若仅有一个有参构造器,可省略 @Autowired):

1
2
3
4
5
6
7
8
9
10
@Service
public class UserService {
private final UserDao userDao; // 强制依赖(final 确保初始化)

// 构造器注入(Spring 4.3+ 可省略 @Autowired)
@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
(2)@Qualifier:解决歧义性

@Autowired 按类型匹配到多个 Bean 时,需用 @QualifierBean id 明确指定注入哪个,与 @Autowired 配合使用。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 两个 UserDao 实现类(均被扫描注册)
@Repository("userDaoMysql") // Bean id = userDaoMysql
public class UserDaoMysqlImpl implements UserDao { ... }

@Repository("userDaoOracle") // Bean id = userDaoOracle
public class UserDaoOracleImpl implements UserDao { ... }

// 2. @Autowired + @Qualifier 明确注入 userDaoMysql
@Service
public class UserService {
@Autowired
@Qualifier("userDaoMysql") // 按 Bean id 注入
private UserDao userDao;
}
(3)@Resource:JSR-250 标准注解

@Resource 是 Java EE 标准注解(非 Spring 原生),支持按 byNamebyType 匹配,优先级:

  1. 若指定 name 属性,按 name(Bean id)匹配;
  2. 若未指定 name,先按属性名(byName)匹配,失败后按类型(byType)匹配;
  3. 不支持 required=false(依赖必须存在)。

示例:

1
2
3
4
5
6
7
8
9
10
@Service
public class UserService {
// 1. 按 name 匹配(Bean id = userDaoMysql)
@Resource(name = "userDaoMysql")
private UserDao userDao1;

// 2. 未指定 name,先按属性名(userDao2)匹配,失败后按类型
@Resource
private UserDao userDao2;
}
(4)@Inject:JSR-330 标准注解

@Inject 是 JSR-330 标准注解(需导入 javax.inject:javax.inject:1 依赖),功能与 @Autowired 类似:

  • 默认按 byType 匹配;
  • 需配合 @Named(类似 @Qualifier)解决歧义;
  • 不支持 required=false(依赖必须存在)。

示例:

1
2
3
4
5
6
7
8
9
10
11
// 需导入依赖:javax.inject:javax.inject:1
import javax.inject.Inject;
import javax.inject.Named;

@Service
public class UserService {
// @Inject + @Named 明确注入 userDaoMysql
@Inject
@Named("userDaoMysql")
private UserDao userDao;
}

3. 注解配置的歧义性处理

除了 @Qualifier/@Named,还可通过 @Primary 标记 “首选 Bean”,解决 @Autowired 按类型匹配的歧义。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. @Primary 标记首选 Bean(容器优先选择)
@Repository
@Primary // 首选 Bean
public class UserDaoMysqlImpl implements UserDao { ... }

@Repository
public class UserDaoOracleImpl implements UserDao { ... }

// 2. @Autowired 会优先注入 @Primary 标记的 UserDaoMysqlImpl
@Service
public class UserService {
@Autowired
private UserDao userDao; // 注入的是 UserDaoMysqlImpl
}

XML 与注解配置的对比与最佳实践

1. 两种配置方式的对比

维度 XML 配置 注解配置
配置复杂度 繁琐(需手动写标签) 简洁(注解 + 组件扫描)
可读性 集中管理,适合复杂配置 分散在类中,适合简单配置
灵活性 支持动态修改(无需重新编译) 静态配置(修改需重新编译)
适用场景 传统项目、复杂配置(如工厂 Bean) 现代项目、简洁开发(Spring Boot)
学习成本 需记忆标签规则(如 <constructor-arg> 需记忆注解含义(如 @Autowired

2. 最佳实践

  1. 现代项目优先用注解 + JavaConfig:Spring Boot 推荐使用 @Configuration + @ComponentScan + @Autowired,配合自动配置(@EnableAutoConfiguration),几乎无需 XML;

  2. 复杂 Bean 用 @Bean 手动注册:如数据源(DataSource)、连接池等,通过@Bean在配置类中手动注册,逻辑更清晰:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Configuration
    public class DataSourceConfig {
    @Bean // 手动注册 DataSource Bean
    public DataSource dataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUrl("jdbc:mysql://localhost:3306/test");
    dataSource.setUsername("root");
    dataSource.setPassword("123456");
    return dataSource;
    }
    }
  3. XML 与注解混合使用:传统项目可保留核心 XML 配置,同时通过 <context:component-scan> 开启注解扫描,逐步迁移;

  4. 依赖注入优先用构造器注入:构造器注入确保依赖在 Bean 初始化时已就绪(final 字段),避免空指针,且支持单元测试 mock 依赖。

总结

Spring 配置 Bean 的核心是通过元数据(XML / 注解)告诉容器 “如何创建 Bean 及注入依赖”,两种配置方式各有优劣:

  • XML 适合复杂、需动态修改的配置,是 Spring 早期的主流方式;
  • 注解适合简洁、现代的开发,配合 JavaConfig 成为 Spring Boot 及微服务项目的首选

欢迎关注我的其它发布渠道