Java.lang.reflect.Proxy 从调用返回另一个代理会导致分配时出现 ClassCastException
所以我正在使用 geotools,我想我应该代理他们的数据访问类之一并跟踪它在他们的代码中的使用方式。
我编写了一个动态代理并在其中包装了一个FeatureSource(接口),然后一切顺利。然后我还想查看 featureSource 返回的一些传递对象,因为 FeatureSource 所做的主要事情是返回一个 FeatureCollection(FeatureSource 类似于 sql 数据源,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);
}
}
是否可以从代理接口动态返回接口代理,或者我做错了什么? 干杯!
So I'm playing with geotools and I thought I'd proxy one of their data-access classes and trace how it was being used in their code.
I coded up a dynamic proxy and wrapped a FeatureSource (interface) in it and off it went happily. Then I wanted to look at some of the transitive objects returned by the featureSource as well, since the main thing a FeatureSource does is return a FeatureCollection (FeatureSource is analogous to a sql DataSource and featurecollection to an sql statement).
in my invocationhandler I just passed the call through to the underlying object, printing out the target class/method/args and result as I went, but for calls that returned a FeatureCollection (another interface), I wrapped that object in my proxy (the same class but a new instance, shouldn't matter should it?) and returned it.
BAM! Classcast exception:
java.lang.ClassCastException: $Proxy5 cannot be cast to org.geotools.feature.FeatureCollection
at $Proxy4.getFeatures(Unknown Source)
at MyClass.myTestMethod(MyClass.java:295)
the calling code:
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
the 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);
}
}
Is it possible to dynamically return proxies of interfaces from a proxied interface or am I doing something wrong?
cheers!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Class.getInterfaces() 仅返回该类直接实现的接口。您需要一个传递闭包来获取所有接口。
更新
示例:
您可能还需要“解包”作为参数传递的代理。
Class.getInterfaces() returns only the interfaces DIRECTLY implemented by the class. You need a transitive closure to optain all the interfaces.
UPDATE
Example:
You may also need to "unwrapp" the proxies that are passed as arguments.
@maurice-perry 的解决方案对我来说非常有效,我已经投票支持它,但我也想指出,有所需方法的库实现。
我最终使用 Apache Commons 库方法
ClassUtils.getAllInterfaces()
:它非常适合
newProxyInstance
:还有一种 Guava 方法使用:
但是你必须弄清楚如何将
Set
转换为Class< ;?>[]
。如果您是 Guava 爱好者,这也许是微不足道的,但 Apache 已经可以使用了。这两个问题都在此相关线程中进行了说明,获取 a 的所有(派生)接口类。
@maurice-perry's solution worked great for me and I have voted for it, but I did also want to point out that there are library implementations of the needed method.
I ended up implementing this solution with the Apache Commons library method
ClassUtils.getAllInterfaces()
:It works great for that magical second argument in
newProxyInstance
:There is also a Guava approach using:
But then you have to figure out howto convert
Set<TypeToken>
toClass<?>[]
. Trivial perhaps, if you're a Guava buff, but Apache's is ready for use.Both of these were noted in this related thread, get all (derived) interfaces of a class.