Jvm-Sandbox源码分析--启动时加载模块
前言
在上一篇Jvm-Sandbox源码分析--启动简析 简单介绍了一下jvm-sandbox启动流程,在这篇文章中我们来分析一下系统模块和用户的自定义模块在启动时,是怎么加载的。
在上一篇文章启动简析的最后,代码进入默认的模块管理类 DefaultCoreModuleManager.reset()方法
//DefaultCoreModuleManager // 初始化加载所有的模块 public synchronized CoreModuleManager reset() throws ModuleException { // 1. 强制卸载所有模块 unloadAll(); // 2. 加载所有模块 for (final File moduleLibDir : moduleLibDirArray) { // 用户模块加载目录,加载用户模块目录下的所有模块 // 对模块访问权限进行校验 if (moduleLibDir.exists() && moduleLibDir.canRead()) { //初始化模块目录加载器,传入模块lib目录和加载模式attach 默认加载模式就是attach new ModuleLibLoader(moduleLibDir, cfg.getLaunchMode()) .load( new InnerModuleJarLoadCallback(), new InnerModuleLoadCallback() ); } else { logger.warn("module-lib not access, ignore flush load this lib. path={}", moduleLibDir); } } return this; }
可以看到这部分代码主要做了两件事:强制卸载所有模块和加载所有模块,但是启动时候其实是没有加载模块的,所有这部分逻辑其实是会跳过,我们后续到通过命令卸载模块到时候再分析。
加载模块
这里加载的模块有两种类型:
- 1.路径/Users/zhengmaoshao/sandbox/bin/../module 下的系统模块sandbox-mgr-module.jar
- 2.路径/Users/zhengmaoshao/.sandbox-module 下的用户自定义模块
/** * 加载Module * * @param mjCb 模块文件加载回调 * @param mCb 模块加载回掉 */ void load(final ModuleJarLoadCallback mjCb, final ModuleJarLoader.ModuleLoadCallback mCb) { // 开始逐条加载 for (final File moduleJarFile : listModuleJarFileInLib()) { try { mjCb.onLoad(moduleJarFile); new ModuleJarLoader(moduleJarFile, mode).load(mCb); } catch (Throwable cause) { logger.warn("loading module-jar occur error! module-jar={};", moduleJarFile, cause); } } }
1.模块文件加载回调
/** * 用户模块文件加载回调 */ final private class InnerModuleJarLoadCallback implements ModuleJarLoadCallback { @Override public void onLoad(File moduleJarFile) throws Throwable { providerManager.loading(moduleJarFile); } }
最终会通过模块Jar文件加载链ModuleJarLoadingChain去加载文件
不过目前来看实现类都是空的,没有起到什么作用。
2.模块加载回调
//ModuleJarLoader.load void load(final ModuleLoadCallback mCb) throws IOException { boolean hasModuleLoadedSuccessFlag = false; ModuleJarClassLoader moduleJarClassLoader = null; logger.info("prepare loading module-jar={};", moduleJarFile); try { moduleJarClassLoader = new ModuleJarClassLoader(moduleJarFile); final ClassLoader preTCL = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(moduleJarClassLoader); try { hasModuleLoadedSuccessFlag = loadingModules(moduleJarClassLoader, mCb); } finally { Thread.currentThread().setContextClassLoader(preTCL); } } finally { if (!hasModuleLoadedSuccessFlag && null != moduleJarClassLoader) { logger.warn("loading module-jar completed, but NONE module loaded, will be close ModuleJarClassLoader. module-jar={};", moduleJarFile); moduleJarClassLoader.closeIfPossible(); } } }
关键步骤:
- 1.创建模块类加载器
- 2.将当前线程的类加载器从沙箱类加载器设置成模块类加载器
- 3.加载模块
- 4.将当前线程的类加载器从模块类加载器设置成沙箱类加载器
3.加载模块过程
ModuleJarLoader的loadingModules方法中的关键步骤:
- 1.通过ServiceLoader加载工具,从sandbox-mgr-module.jar加载沙箱环境模块接口Module的实现类。
实际就是加载ControlModule,InfoModule,ModuleMgrModule 这三个用于内部操作的类。
ServiceLoader<Module> moduleServiceLoader = ServiceLoader.load(Module.class, moduleClassLoader);
- 2.调用模块加载回调onLoad方法,进入到真正进行模块加载的DefaultCoreModuleManager load方法。
// 这里进行真正的模块加载 load(uniqueId, module, moduleJarFile, moduleClassLoader);
DefaultCoreModuleManager load方法关键步骤:
- 1.实例化模块业务对象,注入@resource资源,包括我们自定义Module中的@Resource资源都是在这个时候注入的,在ControlModule中即是沙箱配置信息ConfigInfo
// 初始化模块信息 final CoreModule coreModule = new CoreModule(uniqueId, moduleJarFile, moduleClassLoader, module); // 注入@Resource资源 injectResourceOnLoadIfNecessary(coreModule);
- 2.设置生命周期
callAndFireModuleLifeCycle(coreModule, MODULE_LOAD);
- 3.因为注解@Information中isActiveOnLoad表示是否在加载时候就激活模块,它的默认值是true, 所以会进入激活模块逻辑,这里需要注意,如果不希望启动时候就激活模块,则设置为false。模块只有在激活之后才能增强目标类。
//如果模块标记了加载时自动激活,则需要在加载完成之后激活模块 markActiveOnLoadIfNecessary(coreModule);
在启动过程中系统模块和自定义模块到加载过程就分析完了,ControlModule,InfoModule,ModuleMgrModule 这三个系统模块提供了一些通过shell命令可以操作的方法。
而在我们通过sh sandbox.sh -p pid语句执行启动脚本sandbox.sh 的时候,最后会执行一个默认命令
# default sandbox_curl "sandbox-info/version" exit
这个命令就在刚刚加载的InfoModule类中
@Command("version") public void version(final PrintWriter writer)
所以在我们完成加载之后,便会看到如下信息。
NAMESPACE : default VERSION : 1.2.1 MODE : ATTACH SERVER_ADDR : 0.0.0.0 SERVER_PORT : 60483 UNSAFE_SUPPORT : ENABLE SANDBOX_HOME : /Users/zhengmaoshao/sandbox/bin/.. SYSTEM_MODULE_LIB : /Users/zhengmaoshao/sandbox/bin/../module USER_MODULE_LIB : /Users/zhengmaoshao/sandbox/sandbox-module;~/.sandbox-module; SYSTEM_PROVIDER_LIB : /Users/zhengmaoshao/sandbox/bin/../provider EVENT_POOL_SUPPORT : DISABLE
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
XrpTool - Ripple区块链对接PHP开发包
XrpTool可以帮助PHP应用快速接入瑞波/Ripple区块链, 即支持部署自有Ripple节点的应用场景,也支持利用公开的Ripple节点广播离线裸交易的轻量级部署场景。XrpTool官方下载地址:http://sc.hubwiz.com/codebag/xrp-php-lib/。 1、开发包概述 XrpTool主要包括以下特性: 全功能的Ripple节点客户端,支持完整的RPC API开发接口 支持离线生成Ripple密钥对和地址,支持Secp256k1和Ed25519密码学算法 支持Ripple交易的离线序列化与离线签名 支持瑞波币/XRP和自发行代币的直接转账,支持代币发行、币币交易、支票签发、资金托管等多种Ripple交易 XrpTool开发包运行在PHP 7.1+环境下, 当前版本1.0.0,主要类及关系如下: XrpTool开发包的主要代码文件清单如下: 代码文件 说明 xrp.php/src/ XrpTool源代码目录 xrp.php/src/RpcClient.php Ripple节点的RPC协议封装类 xrp.php/src/TxProcessor.php Rip...
- 下一篇
Java发送邮件必带超时时间配置
前言 只有光头才能变强。 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 在线上遇到了一个发送邮件的问题,记录一下。 一、先说背景 某一天,小王跟我反馈:“麻烦检查一下线上邮件的发送情况,我这查出来发送失败啦” 我去DB查了一下近期的邮件发送情况,表示:“看着都挺正常的,线上没有异常的情况。可能邮件在redis里边堆积了,还没消费” select * from email order by id desc limit 100 先来说一下我这边发邮件的大致实现方式: 这样做有什么好处?把Redis当做一个消息队列,把请求全部扔到Redis上,这能削峰。机器A/B/C的线程会在一定的间隔内向Redis拉取消息,然后调用邮件接口进行发送。 而我这边会在页面上提供一个功能给业务方查询各类消息是否发送成功,由于发送邮件是一个异步的操作,而前同事在编写的时候又追求实时性。 目前的逻辑是:如果push到Redis是成功的,并且Redis里边没有堆积着消息(说明机器A/B/C能及时处理掉这封邮件),那就认为这封邮件发送成功。 P...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2全家桶,快速入门学习开发网站教程
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装