soudegesu / springboot_jdk9_test

This repository is test repository for migration jdk9 with springboot 1.5.x
0 stars 0 forks source link

ModuleLayerを探る #4

Open soudegesu opened 6 years ago

soudegesu commented 6 years ago

java9の仕様上 ModuleLayer 内でのパッケージの重複は認められない、っていうのはどういうことか、を調べてみるメモ

soudegesu commented 6 years ago

そもそも、 --module-path は今回以下のようにして定義している。

   bootRun {
       doFirst {
           jvmArgs += [
                   "--module-path", classpath.asPath,
                   "--add-modules", "java.sql",
                   "--module", "soudegesu.springboot.jdknine.main.demo/soudegesu.springboot.jdknine.main.demo.DemoApplication"
           ]
           classpath = files()
       }
   }
soudegesu commented 6 years ago

ModuleLayer#boot にbreak pointを設定し、 IntelliJ の bootRun を実行する

soudegesu commented 6 years ago

System.bootLayer に何が設定されているかを確認する

    public static ModuleLayer boot() {
        return System.bootLayer;
    }
soudegesu commented 6 years ago

bootLayer 内に modules がたくさん生成されている

evaluate
soudegesu commented 6 years ago

さらにその中も modules でネストされているようだ

module2
soudegesu commented 6 years ago

sun.launcher.LauncherHelper#loadModuleMainClass で引数に指定された --module (今回の場合 soudegesu.springboot.jdknine.main.demo/soudegesu.springboot.jdknine.main.demo.DemoApplication) を boot layer の中からfindしている

        // main module is in the boot layer
        ModuleLayer layer = ModuleLayer.boot();
        Optional<Module> om = layer.findModule(mainModule);
        if (!om.isPresent()) {
            // should not happen
            throw new InternalError("Module " + mainModule + " not in boot Layer");
        }
        Module m = om.get();
soudegesu commented 6 years ago

ModuleModuleLayerModuleDescripter の関係性を理解したい。あと、ClassLoader。

soudegesu commented 6 years ago

open moduleで設定していたので、こんな感じになる。 ModuleDescripterには module-info.java で宣言していた情報が格納されている。 requires もこの通り.

open_module
soudegesu commented 6 years ago

automatic module だとこんな感じ。 java.base は必ず requires されるのか。

automatic
soudegesu commented 6 years ago

今の所、 ModuleLayer はオブジェクトのアドレスが全部いっしょだなぁ。

soudegesu commented 6 years ago

ModuleLayerに登録されているModule全てが参照されているわけではないっぽい

soudegesu commented 6 years ago

http://d.hatena.ne.jp/miyakawa_taku/20171108/1510145001 を参考に ModuleLayerの各メソッドにブレークポイントを貼ったが、 defineModule で止まらないなぁ。

soudegesu commented 6 years ago

javadocにこんな記載が https://docs.oracle.com/javase/jp/9/docs/api/java/lang/ModuleLayer.html

レイヤー内の各Moduleは、そのModuleDescriptorで記述されたパッケージをexportsとopensが作成するように作成されます。 (パッケージがすべてのモジュールではなくターゲット・モジュールのセットにエクスポートされる)は次のようにレイヤーを作成するときに修飾されたエクスポートです:

モジュールXがパッケージをYにエクスポートし、ランタイムModule XがModule Yを読み取る場合、パッケージはModule Y (これはXと同じレイヤーまたは親レイヤーにあります)にエクスポートされます。
モジュールXがパッケージをYにエクスポートし、ランタイムModule XがYを読み取らない場合、Yは、findModuleを呼び出してレイヤーまたはその親レイヤー内のモジュールを検索するかのように配置されます。 Yが見つかった場合、パッケージはYのインスタンスにエクスポートされます。 Yが見つからない場合、修飾されたエクスポートは無視されます。
soudegesu commented 6 years ago

Automcatic Module に関しても以下のような記載が

Configurationを作成するときと同様に、automaticモジュールは、レイヤーを作成するときに特別な処理を受け取ります。 Java仮想マシン内で、Java仮想マシン内の無名の Moduleをすべて読み取るModuleとして、自動モジュールが作成されます。
soudegesu commented 6 years ago

System#initPhase2 -> ModuleBootstrap#boot がmodule systemの初期化処理

soudegesu commented 6 years ago

処理中にこんなものあある

        // --module-path option specified to the launcher
        ModuleFinder appModulePath = createModulePathFinder("jdk.module.path");
soudegesu commented 6 years ago

-m オプションで モジュール指定してアプリケーションを起動すると、 そのモジュールが root module にaddされるようだ。

       // launcher -m option to specify the main/initial module
        String mainModule = System.getProperty("jdk.module.main");
        if (mainModule != null)
            roots.add(mainModule);
soudegesu commented 6 years ago

引数に指定された --add-module もroot moduleに追加されるようだ

        for (String mod: getExtraAddModules()) {
            switch (mod) {
                case ALL_DEFAULT:
                    addAllDefaultModules = true;
                    break;
                case ALL_SYSTEM:
                    addAllSystemModules = true;
                    break;
                case ALL_MODULE_PATH:
                    addAllApplicationModules = true;
                    break;
                default :
                    roots.add(mod);
            }
        }
soudegesu commented 6 years ago

Configuration を作ってあられる

        // run the resolver to create the configuration
        Configuration cf = SharedSecrets.getJavaLangModuleAccess()
                .resolveAndBind(finder,
                                roots,
                                needPostResolutionChecks,
                                traceOutput);
soudegesu commented 6 years ago

#resolveAndBind はこんな感じ

   /**
     * Resolves a collection of root modules, with service binding, and with
     * the empty configuration as its parent. The consistency checks
     * are optionally run.
     *
     * This method is used to create the configuration for the boot layer.
     */
    static Configuration resolveAndBind(ModuleFinder finder,
                                        Collection<String> roots,
                                        boolean check,
                                        PrintStream traceOutput)
    {
        List<Configuration> parents = List.of(empty());
        Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput);
        resolver.resolve(roots).bind();

        return new Configuration(parents, resolver, check);
    }
soudegesu commented 6 years ago

Resolver#resolve の処理の中にこんなものがある

            // if the module is an automatic module then all automatic
            // modules need to be resolved
            if (descriptor.isAutomatic() && !haveAllAutomaticModules) {
                addFoundAutomaticModules().forEach(mref -> {
                    ModuleDescriptor other = mref.descriptor();
                    q.offer(other);
                    if (isTracing()) {
                        trace("%s requires %s", descriptor.name(), nameAndInfo(mref));
                    }
                });
                haveAllAutomaticModules = true;
            }
soudegesu commented 6 years ago

bootLoaderは全てのモジュールが入っているぽい

    /**
     * Load/register the modules to the built-in class loaders.
     */
    private static void loadModules(Configuration cf,
                                    Function<String, ClassLoader> clf) {
        for (ResolvedModule resolvedModule : cf.modules()) {
            ModuleReference mref = resolvedModule.reference();
            String name = resolvedModule.name();
            ClassLoader loader = clf.apply(name);
            if (loader == null) {
                // skip java.base as it is already loaded
                if (!name.equals(JAVA_BASE)) {
                    BootLoader.loadModule(mref);
                }
            } else if (loader instanceof BuiltinClassLoader) {
                ((BuiltinClassLoader) loader).loadModule(mref);
            }
        }
    }
soudegesu commented 6 years ago
    /**
     * Register a module this class loader. This has the effect of making the
     * types in the module visible.
     */
    public void loadModule(ModuleReference mref) {
        String mn = mref.descriptor().name();
        if (nameToModule.putIfAbsent(mn, mref) != null) {
            throw new InternalError(mn + " already defined to this loader");
        }

        LoadedModule loadedModule = new LoadedModule(this, mref);
        for (String pn : mref.descriptor().packages()) {
            LoadedModule other = packageToModule.putIfAbsent(pn, loadedModule);
            if (other != null) {
                throw new InternalError(pn + " in modules " + mn + " and "
                                        + other.mref().descriptor().name());
            }
        }

        // clear resources cache if VM is already initialized
        if (VM.isModuleSystemInited() && resourceCache != null) {
            resourceCache = null;
        }
    }