尝试更改CGLib代理字段值的异常

时间:2014-06-13 23:34:36

标签: java proxy cglib

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

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

public class Person {

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

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

这是提升上述异常(更具体地说是第一行)的代码片段(在“MethodInterceptor”的“拦截”方法内):

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");
....

您是否知道以其他方式访问所需字段或更改其值?

感谢。

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

尝试:

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

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

答案 2 :(得分:1)

即使您已经找到了解决问题的方法,但这里有一个关于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仍然可以调用的原始方法。由于拦截,使用cglib时调用getMethodgetDeclaredMethod按预期工作。但是,字段不会被继承,这就是您需要在一个类中浏览类层次结构的原因。这就是原因:

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

的工作原理。请注意,不再维护cglib。如果您正在寻找替代方案,请查看我的图书馆Byte Buddy。但请注意,我将在下周某个时候发布一个完全稳定的版本。当前的v0.1版本包含一些过早的功能。