Alternativas a java.lang.reflect.Proxy para crear proxies de clases abstractas(en lugar de interfaces)
dynamic-proxy (2)
De acuerdo con la documentación :
[
java.lang.reflect.
]Proxy
proporciona métodos estáticos para crear clases e instancias de proxy dinámico, y también es la superclase de todas las clases de proxy dinámico creadas por esos métodos.
El método newProxyMethod
(responsable de generar los proxies dinámicos) tiene la siguiente firma:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
Desafortunadamente, esto evita que uno genere un proxy dinámico que extienda una clase abstracta específica (en lugar de implementar interfaces específicas). Esto tiene sentido, considerando que java.lang.reflect.Proxy
es "la superclase de todos los proxies dinámicos", lo que impide que otra clase sea la superclase.
Por lo tanto, ¿hay alguna alternativa a java.lang.reflect.Proxy
que pueda generar proxies dinámicos que hereden de una clase abstracta específica, redirigiendo todas las llamadas a los métodos abstractos al manejador de invocación?
Por ejemplo, supongamos que tengo una clase abstracta Dog
:
public abstract class Dog {
public void bark() {
System.out.println("Woof!");
}
public abstract void fetch();
}
¿Hay alguna clase que me permita hacer lo siguiente?
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
Lo que puede hacer en ese caso es tener un controlador de proxy que redirija las llamadas a los métodos existentes de su clase abstracta.
Usted, por supuesto, tendrá que codificarlo, sin embargo, es bastante simple. Para crear su Proxy, tendrá que darle un InvocationHandler
. Luego, solo tendrá que verificar el tipo de método en el método invoke(..)
de su controlador de invocación. Pero cuidado: tendrá que verificar el tipo de método con el objeto subyacente asociado a su controlador y no con el tipo declarado de su clase abstracta.
Si tomo como ejemplo su clase de perro, el método de invocación de su manejador de invocación puede verse así (con una subclase de perro asociada existente llamada ... bueno ... 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 ?
}
}
Sin embargo, hay algo que me mantiene preguntándome: he hablado sobre un objeto de dog
. Pero, como la clase Dog es abstracta, no puede crear instancias, por lo que tiene subclases existentes. Además, como revela una inspección rigurosa del código fuente del Proxy, puede descubrir (en Proxy.java:362) que no es posible crear un Proxy para un objeto Class que no represente una interfaz).
Entonces, aparte de la realidad , lo que quieres hacer es perfectamente posible.
Se puede hacer usando Javassist (ver ProxyFactory
) o CGLIB .
El ejemplo de Adam usando el Javassist:
Yo (Adam Paynter) escribí este código usando 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();
Que produce esta salida:
Woof! Handling public abstract void mock.Dog.fetch() via the method handler