简体   繁体   中英

How to modify/decorator an object returned from 3rd party API using javassist/CGLib

I have an 3rd party API call which returns the following object:

public class A {
  protected void common() {
     System.out.println("common is called in A");
  }

  public void test1() {
    common();
    System.out.println("test1 is called in A");
  }
  public void test2() {
    common();
    System.out.println("test2 is called in A");
  }
}

But I'd like to modify its behavior like the following ModifiedA shows:

public class ModifiedA extends A {
  @Override
  protected void common() {
     super.common();
     System.out.println("common is called in ModifiedA");
  }
}

So what I am trying to do is:

A a = 3rdPartyAPI_call();

//
// Now I'd like to get a ModifiedA which has changed common() behavior.
//

How to use javassist/CGLIB to accomplish this ?

One easy way may be to like this:

public class ModifiedA extends A {
  private A a;
  public ModifiedA(final A a) {
     this.a = a;
  }
  //
  // Override every public method in A
  //

  @Override
  protected void common() {
     super.common();
     System.out.println("common is called in ModifiedA");
  }
}

But since A's definition comes from 3rd party and is very complex and may change, so I'd like to use a proxy to do this?

Thanks for your comments in adavance.

You can use CGLib to implement a delegator pattern without having to override all the methods. There are a few different approaches to implement this depending on style but here is one similar to your example.

You can wrap the instance using a cglib Enhancer :

public static <T> T wrapInstance(final T original) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(original.getClass());
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            Object returnValue = proxy.invoke(original, args);
            if (method.getName().equals("common")) {
                System.out.println("common is called");
            }
            return returnValue;
        }
    });
    return (T) enhancer.create();
}

eclps post will fullfill your requirement and it works.I want to add some more code to eclps code.

Adding filter which give index zero for common method and rest all method to One. MethodInterceptor callback will intercept only common method and rest all method use NoOp intercetor(which will call super class apis).This way filtering is not happening for every method call.

public static <T> T wrapInstance(final T original) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(original.getClass());
        enhancer.setCallbackFilter(new CallbackFilter() {
            @Override
            public int accept(Method method) {
                if (method.getName().equals("common")) {
                    return 0;
                }
                return 1;
            }
        });
        enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                // only common method will intercept this call back.
                return proxy.invoke(this, args);
            }
        }, NoOp.INSTANCE});
        return (T) enhancer.create();
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM