4

I created a CGLib dynamic proxy of a class, but when I try to access any field declared in the original class, I obtain java.lang.NoSuchFieldException. I need to obtain the field in order to change its value.

By the way, this is the class the proxy is based on:

public class Person {

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

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

And this is the code snippet (inside the "intercept" method of the "MethodInterceptor") that is raising the mentioned exception (more specifically the first line):

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

Do you know any other way to access the needed field or change its value?

Thanks.

csfb
  • 1,105
  • 1
  • 9
  • 19

3 Answers3

3

Apparently, a CGLib proxy is a subclass of the original class. So, the following code worked well:

Field field = instance.getClass().getSuperclass().getDeclaredField("name");
csfb
  • 1,105
  • 1
  • 9
  • 19
1

Try:

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

As mentioned in this SO answer, getField only works for public fields, but applies to the entire class hierarchy. You can think of it as inspecting the public interface of the class. getDeclaredField works for private fields, and will not inspect the class hierarchy; you can think of it as resolving the implementation of the class.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Thank you for the hint. I changed for "getDeclaredField" as you suggested, and I obtained a variety of fields related to CGLib. However the problem still persists, since none of the returned fields is the "name" field. – csfb Jun 14 '14 at 12:07
1

Even though you already figured out how to fix your problem, here is a short explanation of how cglib works and what is causing you problems. Considering your Person class, cglib creates another class at runtime which is representing your proxy. This class would approximately look like the following in Java source code, however, many of the instances used are cached which is why cglib adds several other fields. Furthermore, the MethodInterceptor is injected by using different static fields:

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);
  }

  // ...
}

As you can see, the interception is implemented by overriding any method. This way, your MethodInterceptor is invoked instead of the original method which is still invokable by using the MethodProxy. Due to the interception, calling getMethod or getDeclaredMethod works as expected when using cglib. Fields are however not inherited which is why you need to browse the class hierarchy one class up. This is why:

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

works. Note that cglib is not longer maintained. Have a look at my library Byte Buddy in case that you are looking for an alternative. Note however that I am releasing a fully stable version sometime next week. The current v0.1 release contains some premature features.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192