2010-12-10 16 views
11

La clase Proxy JDK solo acepta interfaces en el método de fábrica newProxyInstance().¿Por qué JDK Dynamic Proxy solo funciona con interfaces?

¿Hay una solución disponible o implementaciones alternativas? Los casos de uso son limitados si tengo que extraer métodos a una interfaz para habilitarlos para su uso con un proxy. Me gustaría envolverlos para aplicar acciones basadas en anotaciones durante el tiempo de ejecución.

public static <T> T getProxy(T obj) { 
    InvocationHandler ih = new InjectProxy(obj); 
    ClassLoader classLoader = InjectProxy.class.getClassLoader(); 
    return (T) Proxy.newProxyInstance(classLoader, obj.getClass().getInterfaces(), ih); 
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
} 
+1

Bueno, en principio puede usar una biblioteca de códigos de bytes como ASM para crear un proxy de clase usted mismo. –

+0

@Krik, algunas bibliotecas de simulación de prueba unitaria hacen esto. Necesita crear una clase derivada que anule los métodos que desee. (ya sea automática o manualmente) –

+0

¿Has considerado Aspect-J que está diseñado para hacer este tipo de cosas? –

Respuesta

15

Usted puede utilizar cglib así:

import java.lang.reflect.Array; 
import java.lang.reflect.Method; 
import java.lang.reflect.Modifier; 

import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy; 


public class AbstractFactory { 

    @SuppressWarnings("unchecked") 
    public static <A> A createDefaultImplementation(Class<A> abstractClass) { 
     Enhancer enhancer = new Enhancer(); 
     enhancer.setSuperclass(abstractClass); 
     enhancer.setCallback(new MethodInterceptor() { 
      public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 
       if (!Modifier.isAbstract(method.getModifiers())) { 
        return methodProxy.invokeSuper(proxy, args); 
       } else { 
        Class type = method.getReturnType(); 
        if (type.isPrimitive() && !void.class.equals(type)) { 
         return Array.get(Array.newInstance(type, 1), 0); 
        } else { 
         return null; 
        } 
       } 
      } 
     }); 
     return (A) enhancer.create(); 
    } 

    @SuppressWarnings("unchecked") 
    public static <A> A createDefaultImplementation(String className) throws ClassNotFoundException{ 
     return (A) createDefaultImplementation(Class.forName(className)); 
    } 
} 

Esto, por ejemplo, le permite crear clases abstractas con un método de implementación por defecto. Pero puedes cambiar el potenciador a lo que quieras.

5

¿Hay una solución alternativa disponible ..?

Sí. Ahi esta. Extrae la interfaz de las clases existentes.

UPD

Si lo necesita para algunas clases específicas, se puede escribir como SMT

//interface that already exists 
public interface IDomain { 
    String foo(); 
} 
//your class 
public class Domain implements IDomain{ 
    public String foo(){ 
     return "domain foo"; 
    } 
//method that doesn't placed in IDomain 
    public String bar(){ 
     return "domain bar"; 
    } 
} 
//So you need create new interface with bar() 
//it can extend IDomain 
public interface ExtendedIDomain extends IDomain { 
    public String bar(); 
} 
//than your wrapper factory will be like this 
public class Proxifier { 
    public static ExtendedIDomain getProxy(Domain obj) { 
     InvocationHandler ih = new InjectProxy(obj); 
     ClassLoader classLoader = InjectProxy.class.getClassLoader(); 
     return (ExtendedIDomain) Proxy.newProxyInstance(classLoader, new Class[]{ExtendedIDomain.class}, ih); 
    } 

    static class InjectProxy implements InvocationHandler { 
     private final Domain domain; 
     private InjectProxy(Domain domain){ 
      this.domain = domain; 
     } 

     public String invoke(Object proxy, Method method, Object[] args) throws Throwable{ 
      for(Method m : domain.getClass().getMethods()){ 
       //TODO: check signature(name, args etc) or implement some logic with annotations 
       if(m.getName().equals(method.getName())){ 
        return "wrapped " + m.invoke(domain, args); 
       } 
      } 
      throw new IllegalArgumentException(); 
     } 
    } 
} 
//test 
    public static void main(String[] args) { 
     ExtendedIDomain d = Proxifier.getProxy(new Domain()); 
     System.out.println(d.foo()); 
     System.out.println(d.bar()); 
    } 

Si necesita un poco de cosas "universal" que puedes usar como AOP @Peter Lawrey allready dijo .

+1

Gracias, pero como ya mencioné: "Los casos de uso son limitados si tengo que extraer métodos a una interfaz para habilitarlos para su uso con un proxy" – stacker

+0

@stacker, parece que no necesita ningún tipo de utilidad, solo soluciono un problema concreto. He actualizado mi publicación para este propósito. –

Cuestiones relacionadas