有时,在我们部署应用程序并卸载和安装 bundle 后,我们会遇到类转换异常,其中类 A 无法转换为类 A。问题是实例的类加载器已经在一段时间的内存不同于类本身的类加载器。部署不会影响存储实例的包(在此示例中为包 Y)。
伪代码如下:
捆绑X
public class A extends B {
/* ... */
}
...
/* ... */
@Reference
private InMemoryUserTokenStore inMemoryUserTokenStore;
/* ... */
protected UsersTokenStore getTokenStore() {
return inMemoryUserTokenStore; // <- reference to service from another bundle where tokens are stored
}
/* The token is created and obtained in the same bundle */
A token = new A(...);
getTokenStore().addToken(token)
/* ... */
B token = getTokenStore().getToken(id)
((A) token).doSomething(); // <- this is when class cast exception is thrown*/
使用调试器我发现这里token
的类名是A
,两者的类加载器返回相同的bundle名称和id(bundle X,相同的 ID)但是它们不相等。
捆绑Y
public class InMemoryUserTokenStore implements UsersTokenStore {
/* ... */
private ConcurrentMap<String, B> tokens = new ConcurrentHashMap();
/* ... */
public B getToken(String id) {
/* ... */
return tokens.get(id); // <- instance returned here sometimes has different class loader than class A from bundle **X**
}
/* ... */
}
我不确定这是 OSGI 的问题还是我们这边的一些设计错误?
最佳答案
如果所讨论的两个类实例具有不同的类加载器,则抛出 ClassCastException。
当更新或卸载 bundle 并导出包时,这些包不会被删除,并且针对旧的已卸载 bundle 解析的 bundle 仍将使用旧的/过时的代码,直到包被刷新。这是 OSGi 规范定义的正确行为。
Sling(AEM 的底层框架)的 bundle installer 实现在安装、更新或删除后调用刷新,因此根本不应该出现此问题。您应该调查为什么在某些情况下刷新失败 - 一个起点是为以下类启用跟踪日志记录并查看发生了什么 -
org/apache/sling/installer/core/impl/tasks/BundleUpdateTask
org/apache/sling/installer/core/impl/tasks/RefreshBundlesTask
关于java - 同一 OSGI 包中的多个类加载器导致类转换异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52272319/