java中可通过代理模式(jdk动态代理、cglib或bytebuddy)或propertychangesupport封装实现属性变更监听:前者拦截setter调用注入监听逻辑,后者通过可观测包装类委托并触发事件,兼顾解耦与可维护性。

Java 中没有内置的属性变更监听机制,但可以通过代理模式(特别是动态代理)在封装属性时实现对属性变化的监听。核心思路是:不直接暴露字段,而是通过代理对象拦截 setter 调用,在赋值前后触发监听回调。
用 JDK 动态代理拦截 setter 方法
适用于接口定义明确的场景。需让被代理类实现一个接口(如 Person 接口),然后用 InvocationHandler 拦截所有方法调用,识别 setter 并注入监听逻辑。
- 定义接口(如
Person)包含setName(String)、setAge(int)等方法 - 真实类(
PersonImpl)实现该接口并持有实际字段 - 自定义
PropertyChangeHandler实现InvocationHandler,在invoke()中判断是否为 setter 方法(如方法名以set开头、参数个数为 1) - 调用前保存旧值,调用后触发
onPropertyChanged(propertyName, oldValue, newValue)
用 CGLIB 或 ByteBuddy 实现无接口代理
当目标类无法或不便实现接口时,可使用 CGLIB(基于继承)或 ByteBuddy(更现代、支持 JDK 9+)生成子类代理,重写 setter 方法。
- CGLIB 的
MethodInterceptor可捕获所有非final方法调用;需注意避免无限递归(调用super时用method.invokeSuper()) - ByteBuddy 更灵活,可精准增强指定 setter,还能自动提取属性名(通过方法名解析或注解标记)
- 二者均要求目标类不能是
final,且 setter 需为public或protected
结合注解与反射实现轻量级监听
若不想引入代理库,也可在封装层手动实现监听,配合注解提升可维护性。
- 定义
@Observable注解标注需要监听的字段或 setter 方法 - 在 setter 内部统一调用
firePropertyChange("name", oldVal, newVal) - 用
PropertyChangeListener接口 +PropertyChangeSupport(JDK 自带)管理监听器列表和事件分发 - 优势是零依赖、调试直观;缺点是需人工编写每个 setter,不适合大量属性
推荐实践:组合使用 PropertyChangeSupport + 封装类
最实用、低侵入的方式是:保持 POJO 简洁,另建一层“可观测包装类”(如 ObservablePerson),内部委托给真实对象,并利用 PropertyChangeSupport 统一管理监听。
- 真实对象(
Person)只含字段和普通 getter/setter,不耦合监听逻辑 - 包装类(
ObservablePerson)持有PropertyChangeSupport实例,每个 setter 先记录旧值,再调用真实对象 setter,最后触发事件 - 外部通过
addPropertyChangeListener("name", listener)订阅,解耦清晰,易于单元测试
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











