【Java】模块

模块是什么?

    • パッケージの上位概念

 

    • 構成

パッケージ群と関連するリソース
自身の構成情報を規定するモジュール定義ファイル

传统的包装问题

无法在包级别上设置访问权限

    • ライブラリ内部での利用を想定したパッケージの場合、ライブラリ外部からアクセスできないようにしたい

 

    しかし内部用途のpublicクラスを不可視にする手段はない

.jar文件无法表示依赖关系

    • 複数のパッケージを.jar形式の圧縮ファイルとしてまとめ、ライブラリとして提供

 

    • しかし.jarではライブラリ間の依存関係は表現できない

 

    .jarの中でどの型のAPIが外部からの利用を想定しているのか、動作のためにどのライブラリが必要かわからない

利用模块来解决!

    • 特定のライブラリ、フレームワークをグループにして束ねる

 

    • モジュール化されたライブラリではpublicクラスもより細かな管理が可能

現在のモジュールの中だけでpublic
特定のモジュールに対してのみpublic
全モジュールに対してpublic (従来と同じpublic)

模块基本

    • /sec直下のmodule-info.javaでモジュール定義ファイル記述

モジュールが依存するパッケージ、外に公開するパッケージ情報を列挙
必要ライブラリをrequiresで宣言してないとThe type java.net.http.HttpRequest is not accessible

//モジュール名foo
//fooモジュールではjava.net.httpを必要
module foo {
  requires java.net.http;
  requires mylib;
  requires gson;
}

标准库的模块

    標準ライブラリもモジュール化されている

基本模块

    • java.base

 

    • 標準ライブラリを一個一個requires追加するのは面倒なので、よく利用するパッケージをjava.baseモジュールにまとめられている

java.baseモジュールは暗黙的にロードされるので明示的にrequires宣言不要
Java標準ライブラリ全体を定義したjava.seモジュールもあるが不要なモジュールもロードしてしまう

变动的依赖

推移的:ロードしたモジュールの先でさらに他のモジュールに依存していた場合を依存先もロード

requires transitiveで推移的な依存をいい感じに解決

module java.se { 
  requires transitive java.compiler;
  requires transitive java.datatranfer;
  requires transitive java.desktop;
//略
}

发布包

    • 自分のパッケージを公開したい場合

外部に公開したいパッケージ(lib)、ライブラリ内で利用するパッケージがある(internal)

exportsディレックティブで公開したライブラリのみを外部公開できる

module mylib { 
  exports mylib.lib;
}
//OK
import mylib.lib.MainLib;
//NG! interna;パッケージは不可視
//import mylib.internal.Sublib;

public class ModuleClient {

  public static void main(String[] args) {
    var main = new MainLib();
    main.run();
  }
}
module foo {
  requires java.net.http;
  requires mylib;
  requires gson;
}

只对特定的模块进行包发布

    • exports パッケージ to モジュール

 

    • 以下の場合、libパッケージはfooモジュールにのみ公開される

 

    公開先は,で複数指定可能
module mylib {
  exports mylib.lib to foo;
}

将private方法改为public

ディープリフレクション:リフレクションを利用し強制的にアクセス

モジュールexportsのみではディープリフレクションは不可能

import java.lang.reflect.InvocationTargetException;
import mylib.lib.MainLib;

public class ModuleClient2 {

  public static void main(String[] args) {
    try {
      var clazz = MainLib.class;
      var con = clazz.getConstructor();
      var m = con.newInstance();
      var name = clazz.getDeclaredField("name"); 
      name.setAccessible(true); //エラー
      System.out.println(name.get(m));

    } catch (InstantiationException | IllegalAccessException |
        IllegalArgumentException | InvocationTargetException
        | NoSuchMethodException | SecurityException | NoSuchFieldException e) {
      e.printStackTrace();
    }
  }
}

通过使用opens指令进行包声明来解决!

    • あくまでも実行時のみパッケージ公開する

 

    • リフレクション以外で型を参照する場合はexportsディレクティブも併記

open module mylib {にするとモジュール配下のパッケージを全部open扱いにする

module mylib {
  opens mylib.lib;
  exports mylib.lib;
}

特殊模块

    • 擬似的なモジュール概念も提供されてる

自動モジュール
無名モジュール

自动模块

    • モジュールパスに配置された.jarファイル

module-info.javaを持たないライブラリ

从宣言信息做出决定

    マニフェストファイル(META-INF/MANIFEST.MF)のAutomatic-Module-Name属性で指定された名前をモジュール名にする
Manifest-Version: 1.0
Automatic-Module-Name: hoge.bar

通过.jar文件名来确定

    • 命名規則

拡張子.jarは削除
ハイフン以降の文字が数値/ドットのみの場合、ハイフン以降削除
英数字以外は.に変換
繰り返しドットは単一ドットに、先頭/末尾のドットは除去

ex: hoge-bar-1.0.5.jarはhoge.bar

動作ルール

配下の全パッケージをexports/opens
モジュールパスに登録された全モジュール、無名モジュールをrequires
自動モジュールでexports/opensされたパッケージを他モジュールから利用するにはrequires必要

匿名模块

    • クラスパスに配置された.jarファイル

 

    • モジュール名持たない

 

    • コンパイル時にアプリケーションモジュールからの参照は不可能

自動モジュールからの参照は可能

アプリケーションモジュール:module-info.javaをもるモジュール

プラットフォームモジュール:標準ライブラリを構成するモジュール

与非模块化库共存

モジュール化されたアプリから非モジュールライブラリを利用する
以下のGsonライブラリはJavaオブジェクトをJSON形式の文字列に変換する

java.sqlパッケージ依存
変換対象のJavaオブジェクトにディープリフレクション

Gsonが内部的に利用するjava.sqlモジュールへの参照がないとjava.sql.Timeクラスにもアクセスできない

//NG例 実行時エラー
import com.google.gson.Gson;

public class NoModuleLib {

  public static void main(String[] args) {
    var g = new Gson();
    var a = new Article("Java 11の変更点", "https://codezine.jp/article/corner/751");

    //オブジェクト内容をJSON化した結果を出力
    System.out.println(g.toJson(a));
  }
}
public class Article {
  private String title;
  private String url;

  public Article(String title, String url) {
    this.title = title;
    this.url = url;
  }

  @Override
  public String toString() {
    return String.format("タイトル:%s(%s)",
        this.title, this.url);
  }
}

在执行选项中明确添加模块

    • 実行構成のVM引数で

–add-opens=モジュール名/パッケージ名=アクセス許可するモジュール

–add-modules=java.sql –add-opens=foo/example=gson

fooモジュールのexampleパッケージをgsonライブラリからアクセス可能にする

广告
将在 10 秒后关闭
bannerAds