Jvm-Sandbox源码分析--启动时加载模块-阿里云开发者社区

开发者社区> 开发与运维> 正文

Jvm-Sandbox源码分析--启动时加载模块

简介: 在上一篇Jvm-Sandbox源码分析--启动简析 简单介绍了一下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去加载文件
不过目前来看实现类都是空的,没有起到什么作用。

image

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章