2010-04-15 9 views
7

Así que estoy jugando con geotools y pensé que sería un proxy de una de sus clases de acceso a datos y rastrear cómo se estaba utilizando en su código.Java.lang.reflect.Proxy devuelve otro proxy de los resultados de la invocación en ClassCastException en la asignación

Codifiqué un proxy dinámico y envolví un FeatureSource (interfaz) en él y lo saqué felizmente. Luego, también quería ver algunos de los objetos transitivos devueltos por el featureSource, ya que lo principal que hace un FeatureSource es devolver un FeatureCollection (FeatureSource es análogo a un sql DataSource y featurecollection a un enunciado sql).

en my invocationhandler Acabo de pasar la llamada al objeto subyacente, imprimiendo la clase/método/argumentos y resultado de destino a medida que avanzaba, pero para las llamadas que devolvieron una FeatureCollection (otra interfaz), envolví ese objeto en mi proxy (la misma clase pero una nueva instancia, ¿no debería importar si lo hiciera?) y lo devolvió. BAM! excepción Classcast:

java.lang.ClassCastException: $Proxy5 cannot be cast to org.geotools.feature.FeatureCollection 
    at $Proxy4.getFeatures(Unknown Source) 
    at MyClass.myTestMethod(MyClass.java:295) 

el Código de llamada:

FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = ... // create the FS 
featureSource = (FeatureSource<SimpleFeatureType, SimpleFeature>) FeatureSourceProxy.newInstance(featureSource, features); 
featureSource.getBounds();// ok 
featureSource.getSupportedHints();// ok 

DefaultQuery query1 = new DefaultQuery(DefaultQuery.ALL); 
FeatureCollection<SimpleFeatureType, SimpleFeature> results = featureSource.getFeatures(query1); //<- explosion here 

Proxy:

public class FeatureSourceProxy implements java.lang.reflect.InvocationHandler { 

private Object target; 
private List<SimpleFeature> features; 

public static Object newInstance(Object obj, List<SimpleFeature> features) { 
return java.lang.reflect.Proxy.newProxyInstance(
    obj.getClass().getClassLoader(), 
    obj.getClass().getInterfaces(), 
    new FeatureSourceProxy(obj, features) 
); 
} 

private FeatureSourceProxy(Object obj, List<SimpleFeature> features) { 
this.target = obj; 
this.features = features; 
} 

public Object invoke(Object proxy, Method m, Object[] args)throws Throwable{ 
Object result = null; 
try { 
    if("getFeatures".equals(m.getName())){ 
     result = interceptGetFeatures(m, args); 
    } 
    else{ 
     result = m.invoke(target, args); 
    } 
} 
catch (Exception e) { 
    throw new RuntimeException("unexpected invocation exception: " + e.getMessage(), e); 
} 
return result; 
} 

private Object interceptGetFeatures(Method m, Object[] args) throws Exception{ 
    return newInstance(m.invoke(target, args), features); 
} 

}

¿Es posible volver dinámicamente apoderados de las interfaces de un proxy interfaz o estoy haciendo algo mal? ¡salud!

Respuesta

10

Class.getInterfaces() devuelve solo las interfaces implementadas DIRECTAMENTE por la clase. Necesita un cierre transitivo para optar por todas las interfaces.

ACTUALIZACIÓN

Ejemplo:

private static Class<?>[] getInterfaces(Class<?> c) { 
    List<Class<?>> result = new ArrayList<Class<?>>(); 
    if (c.isInterface()) { 
     result.add(c); 
    } else { 
     do { 
      addInterfaces(c, result); 
      c = c.getSuperclass(); 
     } while (c != null); 
    } 
    for (int i = 0; i < result.size(); ++i) { 
     addInterfaces(result.get(i), result); 
    } 
    return result.toArray(new Class<?>[result.size()]); 
} 

private static void addInterfaces(Class<?> c, List<Class<?>> list) { 
    for (Class<?> intf: c.getInterfaces()) { 
     if (!list.contains(intf)) { 
      list.add(intf); 
     } 
    } 
} 

También puede ser necesario a "unwrapp" los proxies que se pasan como argumentos.

+2

funciona perfectamente !!wow gracias por eso, en retrospectiva, es obvio que debería haber pensado, por alguna razón, pensé que getInterfaces() devolvería * todas las interfaces implementadas, no solo las implementadas directamente por la clase. – matao

0

@ La solución de maurice-perry funcionó muy bien para mí y yo he votado a favor, pero también quería señalar que hay implementaciones de la biblioteca del método necesario.

que terminó la implementación de esta solución con el método de Apache Commons biblioteca ClassUtils.getAllInterfaces():

... 
import org.apache.commons.lang3.ClassUtils; 
... 

private static Class<?>[] getAllInterfaces(Object object) { 
    final List<Class<?>> interfaces = 
     ClassUtils.getAllInterfaces(object.getClass()); 

    return interfaces.toArray(new Class<?>[interfaces.size()]); 
} 

Funciona muy bien para ese segundo argumento mágico en newProxyInstance:

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
         InvocationHandler h) 

También hay un enfoque de guayaba usando :

final Set<TypeToken> tt = TypeToken.of(cls).getTypes().interfaces(); 

Pero luego tienes que averiguar cómo convertir Set<TypeToken> a Class<?>[]. Trivial quizás, si eres un aficionado a la guayaba, pero Apache's está listo para usar.

Ambos se mencionaron en este hilo relacionado, get all (derived) interfaces of a class.

Cuestiones relacionadas