ChatGPT解决这个技术问题 Extra ChatGPT

Alternatives to java.lang.reflect.Proxy for creating proxies of abstract classes (rather than interfaces)

According to the documentation:

[java.lang.reflect.]Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

The newProxyMethod method (responsible for generating the dynamic proxies) has the following signature:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                             throws IllegalArgumentException

Unfortunately, this prevents one from generating a dynamic proxy that extends a specific abstract class (rather than implementing specific interfaces). This makes sense, considering java.lang.reflect.Proxy is "the superclass of all dynamic proxies", thereby preventing another class from being the superclass.

Therefore, are there any alternatives to java.lang.reflect.Proxy that can generate dynamic proxies that inherit from a specific abstract class, redirecting all calls to the abstract methods to the invocation handler?

For example, suppose I have an abstract class Dog:

public abstract class Dog {

    public void bark() {
        System.out.println("Woof!");
    }

    public abstract void fetch();

}

Is there a class that allows me to do the following?

Dog dog = SomeOtherProxy.newProxyInstance(classLoader, Dog.class, h);

dog.fetch(); // Will be handled by the invocation handler
dog.bark();  // Will NOT be handled by the invocation handler

C
Cristian Ciupitu

It can be done using Javassist (see ProxyFactory) or CGLIB.

Adam's example using Javassist:

I (Adam Paynter) wrote this code using Javassist:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Dog.class);
factory.setFilter(
    new MethodFilter() {
        @Override
        public boolean isHandled(Method method) {
            return Modifier.isAbstract(method.getModifiers());
        }
    }
);

MethodHandler handler = new MethodHandler() {
    @Override
    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        System.out.println("Handling " + thisMethod + " via the method handler");
        return null;
    }
};

Dog dog = (Dog) factory.create(new Class<?>[0], new Object[0], handler);
dog.bark();
dog.fetch();

Which produces this output:

Woof!
Handling public abstract void mock.Dog.fetch() via the method handler

+1: Exactly what I need! I will edit your answer with my sample code.
proxyFactory.setHandler() is deprecated. Please use proxy.setHandler.
@axtavt is the object "Dog" an implementation or an interface in the above code?
c
chatellier

What you can do in such a case is having a proxy handler that will redirect calls to existing methods of your abstract class.

You of course will have to code it, however it's quite simple. For creating your Proxy, you'll have to give him an InvocationHandler. You'll then only have to check the method type in the invoke(..) method of your invocation handler. But beware : you'll have to check the method type against the underlying object associated to your handler, and not against the declared type of your abstract class.

If I take as an example your dog class, your invocation handler's invoke method may look like this (with an existing associated dog subclass called .. well ... dog)

public void invoke(Object proxy, Method method, Object[] args) {
    if(!Modifier.isAbstract(method.getModifiers())) {
        method.invoke(dog, args); // with the correct exception handling
    } else {
        // what can we do with abstract methods ?
    }
}

However, there is something that keep me wondering : I've talked about a dog object. But, as the Dog class is abstract, you can't create instances, so you have existing subclasses. Furthermore, as a rigorous inspection of Proxy source code reveals, you may discover (at Proxy.java:362) that it is not possible to create a Proxy for a Class object that does not represents an interface).

So, apart from the reality, what you want to do is perfectly possible.


Please bear with me while I try to understand your answer... In my particular case, I want the proxy class (generated at runtime) to be the subclass of Dog (for example, I'm not explicitly writing a Poodle class that implements fetch()). Therefore, there is no dog variable to invoke the methods upon... Sorry if I'm confusing, I will have to think this over a bit more.
@Adam - you can't dynamically create subclasses at runtime without some bytecode manipulation (CGLib I think does something like this). The short answer is that dynamic proxies support interfaces, but not abstract classes, because the two are very very different concepts. It's almost impossible to think of a way to dynamically proxy abstract classes in a sane way.
@Andrzej: I understand that what I'm asking for requires bytecode manipulation (in fact, I have already written a solution to my problem using ASM). I also understand that Java's dynamic proxies only support interfaces. Perhaps my question wasn't entirely clear - I am asking if there is any other class (that is, something other than java.lang.reflect.Proxy) available that does what I need.
Well, to make long things short ... no (at least in standard Java classes). Using bytecode manipulation, the sky is the limit !
I downvoted because it's not really an answer to the question. OP stated that he wants to proxy a class, not an interface, and is aware that that's not possible with java.lang.reflect.Proxy. You simply repeat that fact and offer no other solution.

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now