Closed cngddflzw closed 5 years ago
先回答 Spring 相关的问题,目前采用两种方案:
1、ark plugin 的去 spring 化,参考 SOFARPC, sofarpc 核心包是和spring无关的,但是为了支持注解及xml配置引用服务的形式,有单独的 rpc-sofa-boot-starter 工程,所有涉及spring的处理放在这个包中。在制作ark插件化时,rpc-sofa-boot-starter 是和 ark biz 依赖引入,共同有bizClassloader加载,而sofarpc 核心包及其间接依赖单独成为 ark plugin.
2、扩展 pluginclassloader 逻辑,遇到spring相关类,委托给 biz classloader. 这样就统一了biz和plugin共同的spring,这种方案社区已经有公司反馈内部是这样改造的。
除了 spring 之外, 我还遇到一些第三方库, 里面出现了 Class.forName("MyClass", true, Thread.currentThread().getContextClassLoader()).asSubclass(type) 这样的代码, 其中 MyClass 是基础组件 plugin 中的某个类, 而 type 是 MyClass 的一个 Class 类型引用 (Class type = MyClass.class) 由于上下文的 ClassLoader 是 BizClassLoader, type 的 ClassLoader 是 PluginClassLoader, 会导致 asSubclass() 失败, 类型不匹配
目前plugin 和 biz 启动时,thread context classloader 都会设置成pluginclassloader 或者 bizclassloader, 你上面说的问题应该是对应的父类没有导出所致,通常来说,我们建议每个 Plugin 的导出类最好是面向接口的,而且只导出和本插件自身类,不到处插件依赖的间接二方包类。
除了 spring 之外, 我还遇到一些第三方库, 里面出现了 Class.forName("MyClass", true, Thread.currentThread().getContextClassLoader()).asSubclass(type) 这样的代码, 其中 MyClass 是基础组件 plugin 中的某个类, 而 type 是 MyClass 的一个 Class 类型引用 (Class type = MyClass.class) 由于上下文的 ClassLoader 是 BizClassLoader, type 的 ClassLoader 是 PluginClassLoader, 会导致 asSubclass() 失败, 类型不匹配
目前plugin 和 biz 启动时,thread context classloader 都会设置成pluginclassloader 或者 bizclassloader, 你上面说的问题应该是对应的父类没有导出所致,通常来说,我们建议每个 Plugin 的导出类最好是面向接口的,而且只导出和本插件自身类,不到处插件依赖的间接二方包类。
你说的这点我没太明白, 我确实是没有导出间接依赖的包, 例如我就写了一个这样的配置
<exported>
<packages>
<package>com.test.remoteconfig.client.*</package>
</packages>
</exported>
将客户端的代码都导出了, 然而在执行到某个 Plugin 内部的导出类 com.test.remoteconfig.client.ConfigInitializer 的逻辑时候, 例如
public void ConfigInitializer.init() {
System.out.println(
Class.forName("com.google.common.collect.Lists", true, Thread.currentThread().getContextClassLoader())
.equals(com.google.common.collect.Lists.class));
}
如果 com.google.common.collect.Lists.class 不是导出类的话, 那么这个 equals() 应该是 false 吧, 如果遇到使用这种方法做的类型校验, 就会校验失败导致异常 这样会导致一些问题, 我的想法是给所有 plugin 导出类的 api 做一层代理, 在执行方法之前都先设置 Current Thread ClassLoader 为 PluginClassLoader 来避免这个问题, 你觉得合理吗
ConfigInitalizer.init 方法如果不是在插件启动执行,而是在启动 Biz 的时候触发的,这个时候 ContextClassLoader 是 BizClassLoader,是会存在问题。这种代码很难避免。 目前遇到的这种问题不多,唯一遇到的是 Log4j 配置,不过官方通过配置 ignoreTCL=true 避免,目前ark自动会配置这一项。
这样会导致一些问题, 我的想法是给所有 plugin 导出类的 api 做一层代理, 在执行方法之前都先设置 Current Thread ClassLoader 为 PluginClassLoader 来避免这个问题, 你觉得合理吗
额,这样的话你代码会很丑吧,建议在插件逻辑中较少使用 context loader 去处理
额,这样的话你代码会很丑吧,建议在插件逻辑中较少使用 context loader 去处理
哈哈. 谢谢. 就是因为无法预知哪里会出现类似的问题, 为了一劳永逸, 我打算使用类似于 Javassist 之类的做个 ark 化的中间层, 让客户端 api 层 ark 化以后 client 实现自动包一层代理. 应该也不会很丑, 不需要修改原来的客户端.
总之感谢你的回答, 谢谢~!
@cngddflzw 好的, 如果方案简单通用,可以贡献哈。 另外方便透露下你们公司吗?
这个后来怎么解决的
这个后来怎么解决的
手动设置上下文 classloader 吧
Your question
我的场景是希望使用 ark 来改造基础组件客户端, 隔离基础组件与业务代码之间的第三方依赖冲突, 以此推动基础组件的自动升级.
举个例子, 我的配置中心的客户端, 我们设计很多自定义的 annotation 用来注入远程配置, 例如 @EnableRemoteConfig @MyValue("${k1}") 这样的使用方式, 即可将远程配置注入变量中. 而这些 annotation 的生效, 则依赖于 spring 的一些开关, 例如 @Configuration 以及 ImportAware 等等
假如我直接将整个 client jar 包的类做 ark 导出处理, 例如我在 export 配种配置 package 为 com.test.configcenter.client.* 这样, 则会导致这些 @EnableRemoteConfig 失效
之前我看过 issue 中有提到关于处理 spring 的问题, 作者给出的两种选择是 1. 去 spring 化 (显然这里不合适, 我们就是要用 spring 的 annotation scan 功能) 2. 将 spring 下沉为另一个 ark plugin, 让业务和 plugin 的 spring 类都从该 plugin 中导出
在此我有几个问题, 希望可以帮忙看看
ark 化以后的注解失效, 应该是因为业务项目启动的 spring context 相关的类都是由 BizClassLoader 加载, 而 Plugin 中的 spring 相关的类都由 PluginClassLoader 加载, 导致认不出来这些注解?
spring 下沉为另一个 ark plugin 的做法, 是否就是将依赖的 spring 包都放到一个项目的 pom 的 dependency 中, 然后在 ark 的配置中配置 exported 的 package 为 org.springframework.* ?
下沉 spring 这种处理与业务有交互的第三方库, 感觉也比较难完备的解决业务与插件出现同一个类型的不同 classLoader 交互问题, 除了 spring 之外, 我还遇到一些第三方库, 里面出现了
Class.forName("MyClass", true, Thread.currentThread().getContextClassLoader()).asSubclass(type)
这样的代码, 其中 MyClass 是基础组件 plugin 中的某个类, 而 type 是 MyClass 的一个 Class 类型引用 (Class type = MyClass.class) 由于上下文的 ClassLoader 是 BizClassLoader, type 的 ClassLoader 是 PluginClassLoader, 会导致 asSubclass() 失败, 类型不匹配 我想问的是第三方库中可能会存在很多这种隐式交互, 并且第一次发布的时候可能发现不了 (因为代码可能没跑到那个分支, 就不会触发异常), 有没有比较好的方法来比较全面的规避这种 BizClassLoader 和 PluginClassLoader 出现交互, 导致类型不匹配的问题?Environment
java -version
): 1.8uname -a
): MacOS 10.13.6