@Holger 的评论指出了问题:Foo
isn't Foo
一旦它们都被不同的类/模块加载器加载。
让我概述两种不同的方法:
解决方案一:模块方式
这里的目标是上课Foo
仅加载一次。
为了实现这一点,具有上述代码的应用程序也应该位于一个模块中,并且需要Model
:
module App {
requires Model;
}
为了使这项工作顺利进行,您还必须避免Model
通过所提供的代码第二次加载。这可以通过 (a) 来实现not将其放入"\\model\\lib\\EC"
。或者通过 (b) 改变顺序ModuleFinder
s in Configuration.resolve()
:
Configuration cf = parent.configuration().resolve(ModuleFinder.of(), finder. Set.of("Implementation"));
这边走Implementation
将使用的版本Model
存在于调用代码的模块路径中并且不通过加载它finder
.
解决方案 2:使用代理
这需要Foo
成为一个接口。
你可以有一个方法proxyOf
看起来像这样:
private Foo proxyOf(Object result) {
InvocationHandler handler = (proxy, method, args) -> {
Method delegate = result.getClass().getMethod(method.getName(), method.getParameterTypes());
return delegate.invoke(result, args);
};
return (Foo) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{ Foo.class }, handler);
}
这样你就可以有两个版本Foo
。您只需确保应用程序代码实际上被允许访问方法delegate
。所以它(以及包含的实现类)必须是public
否则您将需要额外的delegate.setAccessible(true);
在调用它之前。如果应用程序位于模块内(如解决方案 1),则包delegate
必须通过opens
clause:
module Implementation {
requires transitive Model;
exports org.example.impl;
opens org.example.impl;
}
请注意,有一个主要陷阱在代理解决方案中:只要您传递 JRE 的类型,它就可以正常工作,但只要您传递自己的类型,它就可以正常工作method.getParameterTypes()
将返回错误的类型,导致NoSuchMethodException
。即使您设法获得正确的类型,您也需要传递该类型的代理。那么这一切都会得到really复杂,你最终会得到自己的小“来回代理”框架。
最后的笔记
不幸的是你没有描述你的内容actually想要实现。所以我们无法知道你想用它解决什么问题以及为什么需要上面的代码。老实说,对我来说,你的代码可以由其他人维护似乎相当困难。也许你可以通过使用来实现同样的事情ServiceLoader https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html?