Search code examples
javareflectionproxyjavassist

Java: Is it possible to add / attach a proxy to a live object instance during runtime?


I know Java supports proxies. I've been checking Javassist but not sure whether it can support the following trick:

public class Hello {

    public void hi() {
        System.out.println("hi");
    }
}

Hello hello = new Hello();

Hello proxyHello = createProxy(hello);
proxyHello.hi(); // method will be intercepted by a predefined proxy

Is it possible to do something like that?


Solution

  • What is available in the JDK only allows to create proxies that implement a set of interfaces, if you want to create a proxy of a Class, you will need to use Javassist but to be able to do it you need a constructor that is accessible from the class where you create your proxy.

    Here is a way to implement what you need:

    final Hello hello = new Hello() {
        public void hi() {
            System.out.println("Hello World");
        }
    };
    ProxyFactory factory = new ProxyFactory();
    factory.setSuperclass(Hello.class);
    MethodHandler handler = (self, m, proceed, args) -> {
        // This allows to proxy even non accessible methods, it could not be
        // needed depending on your context
        if (!m.isAccessible())
            m.setAccessible(true);
        return m.invoke(hello, args);
    };
    Hello proxy = (Hello)factory.create(new Class<?>[0], new Object[0], handler);
    proxy.hi();
    

    Output:

    Hello World
    

    In this example:

    1. First I create my factory
    2. Then I provide the super class
    3. Then I define how to handle all method calls on the proxy (here I delegate everything to my live object)
    4. Finally I create my proxy (here I use the default constructor to create the proxy).

    More details here

    Assuming that you have a constructor with one String argument you will need to modify the last line as next:

    Hello proxy = (Hello)factory.create(
        new Class<?>[]{String.class}, new Object[]{"bar"}, handler
    );
    

    Here I call a constructor with one argument of type String and I provided bar as value.