背景
最近由于业务需求,后端需要满足在程序运行期间动态加载或修改一些Class的行为,这些Class可能有以下特点:
- 一个普通的类
- 一个Spring的bean(依赖注入功能)
- 依赖于其他的动态Class
- 可以动态更替
我们最后实现了一个infra级别的动态加载服务,通过这个服务你可以自定义动态资源,并通过服务提供的工程化的方式管理这些资源,当然,这些资源最终都会以Jar包的形式被加载管理。这篇文章是对服务设计的一个分享
服务实现
下图大概描述了从请求一个资源到资源实例返回这整个流程
- 应用A向动态加载服务请求类型为UserService的动态资源(dynamic resource)
- 服务根据应用A传来的Id从数据库读取数据,dynamic resource在数据库中存储结构参考图左上角json描述。之后通过对config字段内容的hash生成一个版本号version,这个version有两个目的:一是实现版本更替功能,一旦配置改变,reload此资源。二是用来做Spring BeanName
- 每次load一个资源,我们会有一个ResourceDescriptor类对象来描述这个资源,并且这个对象会被缓存。在每次请求中会先查询缓存,若缓存命中走第4步,若缓存未命中直接跳到第6步
- 缓存命中,检查version是否相同,若相同直接走第7步返回,若不相同说明此资源在上次加载之后配置有变,需要reload,走第5步
- 在reload之前我们需要把旧的已过期的资源摧毁掉,这个摧毁主要有三项:
- 在Spring容器中通过beanName删除过期的BeanDefinition,beanName即为version
- 通过缓存中的resourceDescriptor.getClassLoader().close()关闭过期的URLClassLoader
- 通过dynamicResourceId移除ResourceDescriptor缓存
- 加载新资源的过程有下面四步:
- 用资源的jarUrl与dependencyJarUrls组成的urls创建新的URLClassLoader
- 用上步创建的URLClassLoader把配置中指定的class字节码加载到内存
- 向Spring BeanFactory中注册该class的BeanDefinition
- 创建资源描述符ResourceDescriptor,存入缓存
- 调用Spring applicationContext.getBean(version);
- 返回实例到服务A
这样就完成了一个基本的动态加载服务。
注意,这套流程并没有解决两个dynamic resource有依赖的场景