Search code examples
javabytecode

How to change object behavior dynamically in Java, dynamic proxy is useless


Here is a code snippet to explain the question:

public class Demo {
    // I want to enhance this method.
    public String a() {
        return "A";
    }

    // this method is not enhanced but the internal a() invocation should be
    public String b() {
        return "-> " + a();
    }
}

I want to enhance method a(), and when method b() invoked, the enhanced a() should be executed. I guess that a CGLIB proxy takes no effort.

Besides, this enhancement action should be object-level rather than class-level.

public static void verify() {
    assert Objects.equals("-> A", new Demo().b());
    assert !Objects.equals("-> A", enhance(new Demo()).b());
}

public static Demo enhance(Demo demo) {
    throw new UnsupportedOperationException("I don't know how to do");
}

Is there a solution to resolve this problem?

BTW: sorry for my poor English, but I think my intention is described clearly.

Supplement:

Actually, my issue is:

There are exception stack traces will be logged, but some of them have sensitive data in their detailMessage field by using new XXException("sensitive"), or some IOException will print the detailed absolute path.

printStackTrace() will invoke toString() method, and toString() method will invoke getMessage() method. (You can see the JDK source code)

I want to supply a utility method to wrap/enhance the Exception, let printStackTrace() not print the sensitive data but print the stack trace for finding bugs. Then these Exception objects can be sent to log4j safely.


Solution

  • As you mentioned CGLIB proxy (and java dynamic proxies) both have the same issue. When calling another method from a proxied method (in your case when b() invokes a()), the reference 'self' is used, and the proxy never comes into the picture.

    As a work around you may consider passing the proxied object itself to the class, as in this SO Answer. The thread ask a similar question as you where the 'enhancement' is Spring's transaction processing proxy. After that you may use the reference to self everywhere instead of the straight forward invocation.

    You may also consider, refactoring your code, so that such a case does not arise. But then that may or may not be possible depending upon your use case.

    As for the latter question about enhancement action should be object-level rather than class-level java dynamic proxies work on an object level. So if you create a new object of the class and do not proxy it, it will remain the 'enchancement-free'.