Java.lang.riflettere.Proxy restituzione di un altro proxy dall'invocazione risultati in ClassCastException su assegnazione
-
27-09-2019 - |
Domanda
Così sto giocando con geotools e ho pensato di proxy uno dei loro dati di accesso classi di traccia e di come è stato utilizzato nel codice.
Ho codificato un dynamic proxy e avvolta da un FeatureSource (interfaccia) in esso e andarono a vivere felicemente.Poi ho voluto guardare alcune delle transitivo oggetti restituiti dal featureSource, dato che la cosa principale di un FeatureSource non è il ritorno di un FeatureCollection (FeatureSource è analogo a un'Origine dati sql e featurecollection a un'istruzione sql).
nel mio invocationhandler ho appena passato la telefonata attraverso il sottostante oggetto, stampando il target di classe/metodo/opzioni, di risultato e di come sono andato, ma per le chiamate che ha restituito un FeatureCollection (un'altra interfaccia), ho avvolto l'oggetto proxy (la stessa classe, ma di una nuova istanza, non importa dovrebbe?) e lo hanno restituito.BAM!Classcast eccezione:
java.lang.ClassCastException: $Proxy5 cannot be cast to org.geotools.feature.FeatureCollection
at $Proxy4.getFeatures(Unknown Source)
at MyClass.myTestMethod(MyClass.java:295)
il codice chiamante:
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);
}
}
È possibile dinamicamente restituire le deleghe di interfacce da un proxy interfaccia o sto facendo qualcosa di sbagliato?evviva!
Soluzione
Class.getInterfaces () restituisce solo l'interfaccia direttamente implementati dalla classe. Hai bisogno di una chiusura transitiva per optain tutte le interfacce.
UPDATE
Esempio:
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);
}
}
}
Si può anche bisogno di "unwrapp" le deleghe che vengono passate come argomenti.
Altri suggerimenti
@maurice-perry soluzione ha lavorato per me e ho votato a favore, ma l'ho fatto anche far notare che ci sono implementazioni della libreria del metodo.
Ho finito l'implementazione di questa soluzione con l'Apache Commons biblioteca metodo 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()]);
}
Funziona alla grande per quel magico secondo l'argomento in newProxyInstance
:
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h)
C'è anche un Guava utilizzando l'approccio di:
final Set<TypeToken> tt = TypeToken.of(cls).getTypes().interfaces();
Ma poi devi capire howto convertire Set<TypeToken>
per Class<?>[]
.Banale forse, se sei un Guava buff, ma di Apache è pronto per l'uso.
Entrambi questi sono stati notati in questo thread, ottenere tutti (derivato) interfacce di una classe.