java-9-migration-issue
Java 9の移行の問題と解決策
1. 概要
Javaプラットフォームは、モノリシックアーキテクチャを使用しており、すべてのパッケージを単一のユニットとしてバンドルしていました。
Java 9では、これはlink:/java-9-modularity[Java Platform Module System(JPMS)]、または_Modules_の導入により合理化されました。 関連するパッケージはモジュールの下にグループ化され、*モジュールがパッケージを置き換えて再利用の基本単位*になりました。
このクイックチュートリアルでは、*既存のアプリケーションをJava 9 *に移行するときに直面する可能性のあるモジュールに関連するいくつかの問題について説明します。
2. 簡単な例
4つのメソッドを含む単純なJava 8アプリケーションを見てみましょう。これらのメソッドはJava 8で有効ですが、将来のバージョンでは困難です。 これらのメソッドを使用して、Java 9への移行の影響を理解します。
最初のメソッドは、アプリケーション内で参照されているJCEプロバイダーの名前を*フェッチします:
private static void getCrytpographyProviderName() {
LOGGER.info("1. JCE Provider Name: {}\n", new SunJCE().getName());
}
2番目のメソッドは、スタックトレース内のクラスの名前をリストします*:
private static void getCallStackClassNames() {
StringBuffer sbStack = new StringBuffer();
int i = 0;
Class<?> caller = Reflection.getCallerClass(i++);
do {
sbStack.append(i + ".").append(caller.getName())
.append("\n");
caller = Reflection.getCallerClass(i++);
} while (caller != null);
LOGGER.info("2. Call Stack:\n{}", sbStack);
}
3番目のメソッドは、JavaオブジェクトをXMLに変換します*:
private static void getXmlFromObject(Book book) throws JAXBException {
Marshaller marshallerObj = JAXBContext.newInstance(Book.class).createMarshaller();
marshallerObj.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
marshallerObj.marshal(book, sw);
LOGGER.info("3. Xml for Book object:\n{}", sw);
}
最後のメソッドは、JDK内部ライブラリの_sun.misc.BASE64Encoder_を使用して、文字列をBase 64にエンコードします*:
private static void getBase64EncodedString(String inputString) {
String encodedString = new BASE64Encoder().encode(inputString.getBytes());
LOGGER.info("4. Base Encoded String: {}", encodedString);
}
mainメソッドからすべてのメソッドを呼び出しましょう。
public static void main(String[] args) throws Exception {
getCrytpographyProviderName();
getCallStackClassNames();
getXmlFromObject(new Book(100, "Java Modules Architecture"));
getBase64EncodedString("Java");
}
このアプリケーションをJava 8で実行すると、次のものが得られます。
> java -jar target\pre-jpms.jar
[INFO] 1. JCE Provider Name: SunJCE
[INFO] 2. Call Stack:
1.sun.reflect.Reflection
2.com.baeldung.prejpms.App
3.com.baeldung.prejpms.App
[INFO] 3. Xml for Book object:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book id="100">
<title>Java Modules Architecture</title>
</book>
[INFO] 4. Base Encoded String: SmF2YQ==
通常、Javaバージョンは後方互換性を保証しますが、_JPMS_はこれの一部を変更します。
3. Java 9での実行
それでは、このアプリケーションをJava 9で実行しましょう。
>java -jar target\pre-jpms.jar
[INFO] 1. JCE Provider Name: SunJCE
[INFO] 2. Call Stack:
1.sun.reflect.Reflection
2.com.baeldung.prejpms.App
3.com.baeldung.prejpms.App
[ERROR] java.lang.NoClassDefFoundError: javax/xml/bind/JAXBContext
[ERROR] java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder
最初の2つのメソッドは正常に実行され、最後の2つのメソッドは失敗したことがわかります。 *アプリケーションの依存関係を分析して、失敗の原因を調査しましょう*。 Java 9に付属の_jdeps_ツールを使用します。
>jdeps target\pre-jpms.jar
com.baeldung.prejpms -> com.sun.crypto.provider JDK internal API (java.base)
com.baeldung.prejpms -> java.io java.base
com.baeldung.prejpms -> java.lang java.base
com.baeldung.prejpms -> javax.xml.bind java.xml.bind
com.baeldung.prejpms -> javax.xml.bind.annotation java.xml.bind
com.baeldung.prejpms -> org.slf4j not found
com.baeldung.prejpms -> sun.misc JDK internal API (JDK removed internal API)
com.baeldung.prejpms -> sun.reflect JDK internal API (jdk.unsupported)
コマンドからの出力は次のとおりです。
-
最初の列のアプリケーション内のすべてのパッケージのリスト
-
2番目のアプリケーション内のすべての依存関係のリスト
カラム -
Java 9プラットフォームでの依存関係の場所-*これは
モジュール名、または内部JDK API、*またはサードパーティライブラリの場合はなし
4. 廃止されたモジュール
最初のエラー_java.lang.NoClassDefFoundError:javax / xml / bind / JAXBContext._を解決してみましょう。
依存関係リストによると、_java.xml.bind_パッケージは__java.xml.bind module belongs__に属していることがわかります。これは有効なモジュールのようです。 それでは、https://docs.oracle.com/javase/9/docs/api/java.xml.bind-summary.html [このモジュールの公式ドキュメント]を見てみましょう。
公式ドキュメントでは、_java.xml.bind_モジュールは将来のリリースでの削除が推奨されないと述べています。 したがって、このモジュールはデフォルトではクラスパスにロードされません。
ただし、* Javaは、_add-modules_オプションを使用して、オンデマンドでモジュールをロードするメソッドを提供します。 それでは、先に進んで試してみましょう。
>java --add-modules java.xml.bind -jar target\pre-jpms.jar
...
INFO 3. Xml for Book object:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book id="100">
<title>Java Modules Architecture</title>
</book>
...
実行が成功したことがわかります。 このソリューションは迅速かつ簡単ですが、最良のソリューションではありません。
長期的なソリューションとして、https://search.maven.org/search?q = g:javax.xml.bind%20AND%20a:jaxb-apiを追加する必要があります
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
5. JDK内部API
2番目のエラー_java.lang.NoClassDefFoundError:sun / misc / BASE64Encoder._を見てみましょう。
依存関係リストから、_sun.misc_パッケージが_JDK internal_ APIであることがわかります。
内部APIは、その名前が示すように、JDKで内部的に使用されるプライベートコードです。
この例では、**内部APIはJDKから削除されているように見えます** __.___âj“ jdk-internals_オプションを使用して、この代替APIが何であるかを確認しましょう。
>jdeps --jdk-internals target\pre-jpms.jar
...
JDK Internal API Suggested Replacement
---------------- ---------------------
com.sun.crypto.provider.SunJCE Use java.security.Security.getProvider(provider-name) @since 1.3
sun.misc.BASE64Encoder Use java.util.Base64 @since 1.8
sun.reflect.Reflection Use java.lang.StackWalker @since 9
Java 9では、_sun.misc.Base64Encoder._の代わりに_java.util.Base64_を使用することを推奨していることがわかります。したがって、アプリケーションをJava 9で実行するにはコードの変更が必須です。 _ _
Javaプラットフォームが代替を提案しているアプリケーションで使用している他の2つの内部APIがあることに注意してください。ただし、これらのエラーは発生しませんでした。
-
_sun.reflect.Reflection_などの一部の内部API *が考慮されました
プラットフォームにとって重要なため、JDK固有の_jdk.unsupported_モジュールに追加されました。 このモジュールは、Java 9のクラスパスでデフォルトで使用可能です。 -
* _com.sun.crypto.provider.SunJCE_などの内部APIはのみ提供されます
特定のJava実装で。*それらを使用するコードが同じ実装で実行されている限り、エラーはスローされません。すべてのケースで*この例では、内部APIを使用していますが、これは推奨されるプラクティスではありません*。 したがって、長期的な解決策は、プラットフォームで提供される適切なパブリックAPIに置き換えることです。
6. 結論
この記事では、* Java 9で導入されたモジュールシステムが、非推奨APIまたは内部API *を使用する一部の古いアプリケーションで移行問題を引き起こす可能性があることを確認しました。
また、これらのエラーに短期および長期の修正を適用する方法も確認しました。
いつものように、この記事の例はhttps://github.com/eugenp/tutorials/tree/master/core-java-modules/pre-jpms[GitHubで]から入手できます。