Spring 设计模式 07:装饰器模式
本文拆解
BeanWrapper如何通过组合包装 Bean,在属性访问、类型转换、嵌套属性解析等场景中增加能力。
一、装饰器模式在解决什么问题
有时需要在不修改原类的情况下,为对象动态增加能力。
如果直接改原类,会破坏开闭原则;如果靠继承,又容易形成类爆炸。装饰器模式通过组合包装原对象,保持接口一致,同时增加新行为。
二、Spring 里的对应位置
| 设计角色 | Spring 组件 | 作用 |
|---|---|---|
| 基础接口 | PropertyAccessor | 定义属性读写基础能力 |
| 装饰器接口 | BeanWrapper | 扩展获取被包装实例的能力 |
| 具体装饰器 | BeanWrapperImpl | 包装 Bean 并增强属性处理 |
| 被装饰对象 | 任意 Bean 实例 | 保持自身身份不变 |
PropertyAccessor
↑
BeanWrapper
↑
BeanWrapperImpl
↓ 持有
wrappedObject
三、BeanWrapper 增强了什么
直接操作 Bean 属性,可能需要手写反射:
Field field = user.getClass().getDeclaredField("name");
field.setAccessible(true);
field.set(user, "李四");
BeanWrapper 把这些细节收进包装器,同时增加:
- 类型转换;
- 属性验证;
- 嵌套属性访问;
- 属性访问事件等。
四、最小示例
public class User {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
User user = new User("张三", 25);
BeanWrapper wrapper = new BeanWrapperImpl(user);
wrapper.setPropertyValue("name", "李四");
wrapper.setPropertyValue("age", "26");
String name = (String) wrapper.getPropertyValue("name");
Integer age = (Integer) wrapper.getPropertyValue("age");
User original = (User) wrapper.getWrappedInstance();
System.out.println(name); // 李四
System.out.println(age); // 26
System.out.println(user == original); // true
这里体现了装饰器的两个关键特征:
- 被包装对象身份不变;
- 外部通过包装器获得额外能力。
五、装饰器和代理器的区别
虽然 AOP 代理和装饰器都在“包装对象”,但 Spring 中值得区分:
| 维度 | 装饰器模式 | 代理模式 |
|---|---|---|
| 核心目的 | 透明叠加能力 | 控制访问、替换目标 |
| 常见 Spring 场景 | BeanWrapper 属性增强 | AOP 横切增强 |
| 对象身份 | 保持原对象,包装器主动返回 | 调用方通常只看到代理 |
| 扩展方式 | 组合多个装饰器 | 拦截目标方法 |
六、怎么判断“用对了”
- 原对象是否保持可替换,没有被强制继承某个基类;
- 新增能力是否通过组合而不是修改原类;
- 包装器是否保持基础接口,使调用方可以透明使用;
- 是否避免把所有增强都写成一层层继承。
小结
BeanWrapper 是 Spring 中装饰器思想的典型体现。它包装 Bean 实例,在不改变原对象的前提下提供属性访问、类型转换和嵌套解析能力,背后是“接口隔离能力 + 组合实现复用”的设计方式。

422

被折叠的 条评论
为什么被折叠?



