针对没有接口实现的类,使用 Map 工厂管理时NULL问题

40 阅读2分钟
  1. 问题描述
  • 在使用 Map 工厂 管理没有接口实现的类时,通常以 Class 类型作为 key,如 OriginalClass.class。
  • 如果目标类被 CGLIB 代理,实际存储在 Map 中的对象是 CGLIB 生成的代理对象,其 Class 名称与原始类不同,导致通过 OriginalClass.class 无法准确匹配到代理对象。
  1. 问题原因
  • CGLIB 动态生成的代理类是目标类的子类,类名通常类似于 OriginalClass.EnhancerByCGLIB随机字符串。
  • 使用 OriginalClass.class 作为 Map 的 key 时,无法匹配 CGLIB 的代理类。
  1. 解决方案

方案 1:使用 AopProxyUtils 提取原始目标类

在 Spring 环境下,可以使用 AopProxyUtils 提取代理对象的原始目标类并进行匹配:

javaCopy codeimport org.springframework.aop.framework.AopProxyUtils; Class<?> originalClass = AopProxyUtils.ultimateTargetClass(proxyInstance);

将提取的 originalClass 作为 Map 的 key,即可保证获取的对象与存储的代理对象一致。


方案 2:动态注册代理类的 Class

在存储代理对象到 Map 时,动态获取代理对象的真实 Class 并作为 key:

javaCopy codemap.put(proxyInstance.getClass(), proxyInstance);

在查找时,通过判断是否为代理类并获取其真实 Class:

javaCopy codeClass<?> key = proxyInstance instanceof Proxy ? AopProxyUtils.ultimateTargetClass(proxyInstance) : proxyInstance.getClass(); map.get(key);

方案 3:统一管理对象实例

在 Map 工厂中直接存储原始目标类和其代理对象的对应关系:

javaCopy codemap.put(OriginalClass.class, proxyInstance);

通过 getBean() 或类似方法,统一获取代理实例:

javaCopy codeObject bean = map.get(OriginalClass.class);

  1. 注意事项

  2. 避免直接使用

.class 获取实例:如果目标类被代理,直接通过 OriginalClass.class 获取实例会绕过代理逻辑。

  1. 校验代理类

:使用 AopUtils.isAopProxy() 或 instanceof 判断当前对象是否为代理对象:

javaCopy codeif (AopUtils.isAopProxy(proxyInstance)) { System.out.println("This is a proxy object"); }
  1. 代理与原始类匹配一致

:确保在 Map 中存储和查找时,统一使用原始类或代理类的 Class。


结论

对于没有接口实现的类,使用 Map 工厂管理时,如果以 Class 作为 key:

  1. CGLIB 代理会导致原始类的 .class 无法匹配代理对象。
  2. 使用 AopProxyUtils 提取目标类 是推荐的解决方案。
  3. 确保存储和查找的 key 是一致的(原始类或代理类)。