2010-07-20 7 views
65

Según the documentation:Alternativas a java.lang.reflect.Proxy para la creación de proxies de clases abstractas (en lugar de las interfaces)

[java.lang.reflect.] Proxy proporciona métodos estáticos para Creación de clases dinámicas proxy y casos, y también es la superclase de todas las clases de proxy dinámico creadas por esos métodos.

El newProxyMethod method (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 genera un proxy dinámico que extiende una clase abstracta específica (en lugar de implementando 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 puede generar proxies dinámicos que heredan de una clase abstracta específica, volviendo a dirigir todas las llamadas a los métodos abstractos al controlador 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 una clase que me permite 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 

Respuesta

89

Se puede hacer usando Javassist (ver ProxyFactory) o CGLIB.

ejemplo de Adán usando Javassist:

I (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 
+9

+1: ¡Exactamente lo que necesito! Editaré tu respuesta con mi código de muestra. –

+0

'proxyFactory.setHandler()' está en desuso. Por favor use 'proxy.setHandler'. –

-3

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.

Por supuesto tendrá que codificarlo, sin embargo es bastante simple. Para crear su Proxy, tendrá que darle un InvocationHandler. Luego, solo deberá 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 la clase de perro, método de invocación de su manejador de invocación puede tener este aspecto (con una subclase perro asociada existente llamado así .. ... 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 hace pensar: he hablado sobre un objeto 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).

Por lo tanto, aparte de la realidad , lo que quiere hacer es perfectamente posible.

+1

Por favor tengan paciencia conmigo mientras yo intente comprender su respuesta ... En mi caso particular, quiero que la * clase de proxy * (generada en tiempo de ejecución) sea la subclase de 'Perro' (por ejemplo, no estoy escribiendo explícitamente una clase 'Poodle' que implementa' fetch() '). Por lo tanto, no hay una variable 'dog' para invocar los métodos en ... Lo siento si estoy confundido, tendré que pensar esto un poco más. –

+1

@Adam: no se pueden crear subclases dinámicamente en tiempo de ejecución sin la manipulación de algunos bytecode (CGLib creo que hace algo como esto). La respuesta corta es que los proxies dinámicos admiten interfaces, pero no clases abstractas, porque los dos conceptos son muy diferentes. Es casi imposible pensar en una forma de proxy dinámica de las clases abstractas de una manera sensata. –

+0

@Andrzej: Entiendo que lo que estoy pidiendo requiere manipulación de código de bits (de hecho, ya he escrito una solución a mi problema al usar ASM). También entiendo que los proxies dinámicos de Java solo admiten interfaces. Tal vez mi pregunta no estaba del todo clara: estoy preguntando si hay * cualquier otra * clase (es decir, algo que no sea 'java.lang.reflect.Proxy') disponible que haga lo que necesito. –

Cuestiones relacionadas