【问题标题】:Exception trying to change a CGLib proxy field value尝试更改 CGLib 代理字段值的异常
【发布时间】:2014-08-04 14:12:15
【问题描述】:

我创建了一个类的 CGLib 动态代理,但是当我尝试访问在原始类中声明的任何字段时,我得到 java.lang.NoSuchFieldException。我需要获取该字段才能更改其值。

顺便说一下,这是代理所基于的类:

public class Person {

    private String name;
    ....
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    ...
}

这是引发上述异常的代码 sn-p(在“MethodInterceptor”的“intercept”方法内)(更具体地说是第一行):

public Object intercept(Object instance, Method jdkMethod, Object[] args, MethodProxy method) throws Throwable {
...
Field field = instance.getClass().getField("name");
field.setAccessible(true);
field.set(instance, "foo");
....

您知道访问所需字段或更改其值的其他方法吗?

谢谢。

【问题讨论】:

    标签: java proxy cglib


    【解决方案1】:

    试试:

    Field field = instance.getClass().getDeclaredField("name");
    

    this SO answer 中所述,getField 仅适用于公共字段,但适用于整个类层次结构。您可以将其视为检查类的公共接口。 getDeclaredField 适用于私有字段,不会检查类层次结构;您可以将其视为解决类的实现。

    【讨论】:

    • 感谢您的提示。我按照您的建议更改为“getDeclaredField”,并获得了与CGLib相关的各种字段。但是问题仍然存在,因为返回的字段都不是“名称”字段。
    【解决方案2】:

    显然,CGLib 代理是原始类的子类。因此,以下代码运行良好:

    Field field = instance.getClass().getSuperclass().getDeclaredField("name");
    

    【讨论】:

      【解决方案3】:

      即使您已经知道如何解决问题,这里还是简要说明 cglib 的工作原理以及导致问题的原因。考虑到您的 Person 类,cglib 在运行时创建另一个代表您的代理的类。这个类在 Java 源代码中大致如下所示,但是,使用的许多实例都被缓存了,这就是 cglib 添加几个其他字段的原因。此外,MethodInterceptor 是通过使用不同的静态字段注入的:

      public class Person$EnhancedByCglib extends Person {
      
        private static class GetNameMethodProxy extends MethodProxy {
      
          @Override
          public Object invokeSuper(Object instance,
                                    Object[] arguments) {
            return ((Person$EnhancedByCglib) instance).getNameSuper();
          }
      
          // ...
        }
      
        // ...
      
        private static MethodInterceptor methodInterceptor;
      
        @Override
        public String getName() {
          return (String) methodInterceptor.intercept(this, 
                                                      getClass().getDeclaredMethod("getName"),
                                                      new Object[0],
                                                      new GetNameMethodProxy());
        }
      
        private String getNameSuper() {
          return super.getName();
        }
      
        @Override
        public void setName(String name) {
          methodInterceptor.intercept(this, 
                                      getClass().getDeclaredMethod("setName", String.class),
                                      new Object[] {name},
                                      new SetNameMethodProxy());
        }
      
        private void setNameSuper(String name) {
          super.setName(name);
        }
      
        // ...
      }
      

      如您所见,拦截是通过覆盖任何方法来实现的。这样,您的MethodInterceptor 将被调用,而不是使用MethodProxy 仍可调用的原始方法。由于拦截,调用getMethodgetDeclaredMethod 在使用cglib 时按预期工作。然而,字段不会被继承,这就是为什么您需要向上浏览一个类的类层次结构。这就是为什么:

      instance.getClass().getSuperclass().getDeclaredField("name");
      

      有效。请注意,不再维护 cglib。如果您正在寻找替代方案,请查看我的库 Byte Buddy。但是请注意,我将在下周某个时候发布一个完全稳定的版本。当前的 v0.1 版本包含一些不成熟的功能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-06
        • 1970-01-01
        • 2021-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-07
        • 2019-07-11
        相关资源
        最近更新 更多