项目参考:2025/2/28 上午《尚硅谷》——spring中基于xml的自动装配-自动装配(Autowiring)笔记(项目实例演示)-CSDN博客
目录
案例 1:空指针异常(NullPointerException)
案例 2:Bean 定义冲突(NoUniqueBeanDefinitionException)
Spring 基于 XML 的自动装配深度解析
一、实验环境说明
技术栈:Spring Framework 5.x + JUnit 4
项目结构:
- controller
└ UserController(依赖 UserService)
- service
└ UserServiceImpl(实现 UserService 接口,依赖 UserDao)
- dao
└ UserDaoImpl(实现 UserDao 接口)
二、实验案例演示与分析
案例 1:空指针异常(NullPointerException)
配置文件:
<!-- 仅保留 Controller 配置 -->
<bean id="userController" class="com.atguigu.spring.controller.UserController"
autowire="byType"/>
测试代码:
@Test
public void testAutowire() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-autowire-xml.xml");
UserController userController = ioc.getBean(UserController.class);
userController.saveUser(); // 此处抛出 NPE
}
测试结果:
java.lang.NullPointerException
at UserController.saveUser(UserController.java:18)
问题分析:NPE是“NullPointerException”的缩写,指的是空指针异常。
现象 | 原因 | 自动装配机制分析 |
---|---|---|
NPE | UserService 未注入 | byType 查找失败 → 属性值为 null |
测试思路:
-
验证当依赖缺失时自动装配的表现
-
观察 Spring 容器初始化时是否报错
-
测试运行时业务方法的健壮性
关键结论:
-
byType
策略下,容器不会强制要求存在匹配的 bean -
未找到匹配 bean 时属性保持默认值(通常为 null)
-
必须通过单元测试验证依赖注入完整性
案例 2:Bean 定义冲突(NoUniqueBeanDefinitionException)
配置文件:
<!-- Service 层存在两个实现 -->
<bean id="userService" class="com.atguigu.spring.service.impl.UserServiceImpl"/>
<bean id="service" class="com.atguigu.spring.service.impl.UserServiceImpl"/>
<!-- Dao 层存在两个实现 -->
<bean id="userDao" class="com.atguigu.spring.dao.impl.UserDaoImpl"/>
<bean id="dao" class="com.atguigu.spring.dao.impl.UserDaoImpl"/>
异常信息:
NoUniqueBeanDefinitionException:
No qualifying bean of type 'com.atguigu.spring.service.UserService' available:
expected single matching bean but found 2: userService,service
问题分析流程:
-
UserController 尝试通过
byType
注入 UserService -
容器中存在两个 UserServiceImpl 实例
-
Spring 无法决策注入目标 → 抛出明确异常
解决方案对比:
方案 | 实施方式 | 适用场景 | 优缺点 |
---|---|---|---|
移除冗余 Bean | 删除重复的 bean 定义 | 简单项目 | 破坏性修改,降低灵活性 |
使用 primary 属性 |
<bean id="service" primary="true"> |
需要保留多个实现时 | 需显式指定优先级 |
切换为 byName |
修改 autowire="byName" + 匹配属性名与 bean id | 明确命名规范时 | 增加耦合度 |
三、自动装配核心机制解析
知识点:
自动装配:根据指定的策略,在IOC容器中匹配某个bean,自动为bean中的类类型的属性或接口类型的属性赋值可以通过bean标签中的autowire属性设置自动装配的策略。
default
和 byType
自动装配模式的对比表格:
模式 | 描述 |
---|---|
default | 表示不进行自动装配,即 Bean 中的属性不会自动匹配某个 Bean 为属性赋值。此时,属性使用默认值(如基本类型的默认值或对象的 null )。 |
byType | 根据要赋值的属性的类型,在 IOC 容器中查找匹配的 Bean。如果找到匹配的 Bean,则自动为该属性赋值;如果找不到匹配的 Bean,则属性值为 null 。 |
情况 | 行为 |
---|---|
未找到类型匹配的 Bean | 不装配,属性使用默认值(如基本类型的默认值或对象的 null )。 |
找到多个类型匹配的 Bean | 抛出异常:NoUniqueBeanDefinitionException 。 |
总结:当使用byType实现自动装配时,IOC容器中有且只有一个类型匹配的bean能够为属性赋值!
1. 装配策略对照表
策略 | 匹配方式 | 容器行为 | 异常风险 |
---|---|---|---|
default | 无自动装配 | 需要手动配置 | 配置遗漏导致 NPE |
byType | 类型匹配 | 查找唯一匹配 bean | NoUniqueBeanDefinitionException |
byName | ID 名称匹配 | 查找同名 bean | NoSuchBeanDefinitionException |
2. 最佳实践原则
-
接口编程优先:面向接口自动装配降低耦合度
-
单一实现原则:同类型 Bean 尽量保持唯一
-
防御性编程:关键依赖添加
@Required
注解 -
分层验证:
graph TD
A[Controller 单元测试] --> B[验证 Service 注入]
C[Service 单元测试] --> D[验证 Dao 注入]
3. 复杂场景处理方案
场景:需要多个同类型 Bean 时
方案 1:使用 primary
标记主候选
<bean id="mysqlDao" class="com.dao.MySQLDaoImpl" primary="true"/>
<bean id="oracleDao" class="com.dao.OracleDaoImpl"/>
方案 2:结合 autowire-candidate
排除
<bean id="tempDao" class="com.dao.TempDaoImpl"
autowire-candidate="false"/>
方案 3:显式指定依赖名称(byName 策略)
四、工程化建议
1.配置规范:
-
分层定义 Bean(controller/service/dao 分组)
-
采用命名约定(Service 后缀对应服务实现)
2.防御性配置:
<!-- 显式关闭非必要 Bean 的自动装配资格 -->
<bean id="auditService" class="com.special.AuditService"
autowire-candidate="false"/>
3.单元测试模板:
public class BaseSpringTest {
protected ConfigurableApplicationContext context;
@Before
public void initContext() {
context = new ClassPathXmlApplicationContext("spring-config.xml");
}
@After
public void closeContext() {
if(context != null) {
context.close();
}
}
}