7

所以我在玩 geotools,我想我会代理他们的一个数据访问类并跟踪它是如何在他们的代码中使用的。

我编写了一个动态代理并在其中包装了一个 FeatureSource(接口),然后它就愉快地关闭了。然后我还想看看 featureSource 返回的一些传递对象,因为 FeatureSource 所做的主要事情是返回一个 FeatureCollection(FeatureSource 类似于 sql DataSource 和 featurecollection 类似于 sql 语句)。

在我的调用处理程序中,我只是将调用传递给底层对象,打印出目标类/方法/参数和结果,但是对于返回 FeatureCollection(另一个接口)的调用,我将该对象包装在我的代理中(同一个类但是一个新实例,应该没关系吗?)并返回它。砰!类播异常:

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

调用代码:

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

代理:

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);
}

}

是否可以从代理接口动态返回接口代理,或者我做错了什么?干杯!

4

2 回答 2

10

Class.getInterfaces() 只返回类直接实现的接口。您需要一个传递闭包来选择所有接口。

更新

例子:

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);
        }
    }
}

您可能还需要“解包”作为参数传递的代理。

于 2010-04-15T06:49:08.350 回答
0

@maurice-perry 的解决方案对我来说效果很好,我投了赞成票,但我还想指出,有所需方法的库实现。

我最终使用 Apache Commons 库方法实现了这个解决方案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()]);
}

它适用于神奇的第二个参数newProxyInstance

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

还有一种 Guava 方法使用:

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

但是你必须弄清楚如何转换Set<TypeToken>Class<?>[]. 如果您是番石榴爱好者,这可能是微不足道的,但 Apache 已经可以使用了。

这两个都在这个相关线程中提到,get all (derived) interfaces of a class

于 2016-09-16T03:50:23.873 回答