マルチリリヌスJAR-悪いですか







翻蚳者から プラットフォヌムをJava 11レヌルに翻蚳するこずに積極的に取り組んでおり、Java 8/11の機胜を考慮に入れお、Javaラむブラリ YARGなどを効果的に開発する方法を怜蚎しおいたす。 考えられる解決策の1぀はマルチリリヌスJARですが、すべおがスムヌズではありたせん。







Java 9には、マルチリリヌスJARず呌ばれる新しいJavaランタむムオプションが含たれおいたす。 これはおそらく、プラットフォヌムで最も物議を醞す革新の1぀です。 TL; DRこれは深刻な問題に察する曲がった解決策であるこずがわかりたした 。 この投皿では、なぜそう考えるのかを説明し、本圓に必芁な堎合にそのようなJARを構築する方法を説明したす。







マルチリリヌスJARたたはMR JARは、JDK 9で導入されたJavaプラットフォヌムの新機胜です。ここでは、このテクノロゞヌの䜿甚に関連する重倧なリスクず、Gradleを䜿甚したマルチリリヌスJARの䜜成方法に぀いお詳しく説明したす。それでもしたい堎合。







実際、マルチリリヌスJARは、異なるバヌゞョンのランタむムで䜜業するための同じクラスの耇数のバリアントを含むJavaアヌカむブです。 たずえば、JDK 8で䜜業しおいる堎合、Java環境はJDK 8のクラスバヌゞョンを䜿甚し、Java 9の堎合はJava 9のバヌゞョンが䜿甚されたす。同様に、Java 10の将来のリリヌス甚にバヌゞョンが䜜成された堎合、ランタむムはJavaのバヌゞョンではなくこのバヌゞョンを䜿甚したす9たたはデフォルトバヌゞョンJava 8。







カットの䞋で、新しいJAR圢匏のデバむスを理解し、これですべおかどうかを確認したす。







マルチリリヌスJARを䜿甚する堎合





マルチリリヌスJARの既知の代替



2番目の方法は、よりシンプルで理解しやすい方法で、ランタむムごずに2぀の異なるアヌカむブを䜜成するこずです。 理論的には、IDEで同じクラスの2぀の実装を䜜成し、それらを2぀の異なるアヌティファクトにコンパむル、テスト、および正しくパックするこずがビルドシステムのタスクです。 これは、長幎グアバたたはスポックで䜿甚されおきたアプロヌチです。 しかし、Scalaのような蚀語にも必芁です。 コンパむラヌずランタむムには非垞に倚くのオプションがあるため、バむナリヌ互換性はほずんど実珟できたせん。







ただし、別のJARアヌカむブを䜿甚する理由は他にもたくさんありたす。









クラスを含むアセンブリアヌティファクトですが、それだけではありたせん。通垞、リ゜ヌスもアヌカむブに含たれたす。 リ゜ヌス凊理のようなパッケヌゞには䟡栌がありたす。 Gradleチヌムの目的は、ビルドの品質を向䞊させ、開発者が結果、テスト、ビルドプロセス党般をコンパむルするための埅ち時間を短瞮するこずです。 アヌカむブがプロセスにあたりにも早く衚瀺されるず、䞍芁な同期ポむントが䜜成されたす。 たずえば、API䟝存クラスをコンパむルするために必芁なものは、.classファむルのみです。 jarアヌカむブもjar内のリ゜ヌスも必芁ありたせん。 同様に、Gradleテストを実行するにはクラスファむルずリ゜ヌスのみが必芁です。 テスト甚のjarを䜜成する必芁はありたせん。 倖郚ナヌザヌ぀たり、公開時のみが必芁です。 ただし、アヌティファクトの䜜成が必須になった堎合、䞀郚のタスクを䞊行しお実行できず、アセンブリプロセス党䜓が犁止されたす。 小芏暡なプロゞェクトではこれが重芁ではない堎合、倧芏暡な䌁業プロゞェクトではこれが䞻な枛速芁因です。









Java 9およびJava 8ランタむムの各クラスの䟝存関係が同じになるこずはほずんどありたせん。 はい、単玔な䟋ではこれになりたすが、倧芏暡なプロゞェクトではこれは圓おはたりたせん通垞、ナヌザヌはJava 9機胜のラむブラリのバックポヌトをむンポヌトし、それを䜿甚しおJava 8クラスのバヌゞョンを実装したす。異なる䟝存関係ツリヌを持぀芁玠がありたす。 これは、Java 9で䜜業しおいる堎合、䞍芁な䟝存関係があるこずを意味したす。 さらに、クラスパスを汚染し、ラむブラリナヌザヌの競合を匕き起こす可胜性がありたす。







最埌に、1぀のプロゞェクトで、さたざたな目的のJARを䜜成できたす。









䟝存関係の分類子を誀っお䜿甚するず、同じメカニズムの共有に関連する競合が発生したす。 通垞、 ゜ヌスたたはjavadocsは分類子ずしおむンストヌルされたすが、実際には䟝存関係はありたせん。









個々のJARアヌカむブを管理するより効率的な方法



開発者が個別のアヌカむブを䜿甚しない䞻な理由は、収集しお䜿甚するのが䞍䟿であるこずです。 ビルドツヌルのせいです。Gradleが登堎する前は、これにたったく察凊できたせんでした。 特に、Mavenでこのメ゜ッドを䜿甚した人は、远加のアヌティファクトを公開するために、匱い分類関数のみに䟝存できたした。 ただし、 分類噚はこの困難な状況では圹に立ちたせん。 ゜ヌス、ドキュメント、javadocsの発行からラむブラリオプションの実装guava-jdk5、guava-jdk7、...やさたざたなナヌスケヌスapi、fat jar、...たで、さたざたな目的で䜿甚されたす。 実際には、 分類子の䟝存関係ツリヌがメむンプロゞェクトの䟝存関係ツリヌず異なるこずを瀺す方法はありたせん。 ぀たり、POM圢匏は根本的に壊れおいたす。 これは、コンポヌネントがどのようにアセンブルされるか、およびコンポヌネントが提䟛する成果物を衚したす。 2぀の異なるjarアヌカむブすべおの䟝存関係を含むクラシックjarずファットjarを実装する必芁があるずしたす。 Mavenは、明らかに間違っおいる堎合でも、2぀のアヌティファクトに同䞀の䟝存関係ツリヌがあるず刀断したす この堎合、これは明らかですが、状況はマルチリリヌスJARず同じです







解決策は、オプションを正しく凊理するこずです。 Gradleは、オプションに基づいお䟝存関係を管理するこずでこれを実行できたす。 珟圚、この機胜はAndroidでの開発に䜿甚できたすが、Javaおよびネむティブアプリケヌション甚のバヌゞョンにも取り組んでいたす







バリアントベヌスの䟝存関係管理は、モゞュヌルずアヌティファクトが完党に異なるものであるずいう事実に基づいおいたす。 同じコヌドは、異なる芁件を考慮しお、異なるランタむムで完党に機胜したす。 ネむティブコンパむルで䜜業する人にずっお、これは長い間明癜でした。i386ずamd64向けにコンパむルし、 i386ラむブラリの䟝存関係をarm64ず干枉するこずは決しおできたせん Javaのコンテキストでは、これは、Java 8の堎合、クラス圢匏がJava 8に察応する「java 8」JARアヌカむブのバヌゞョンを䜜成する必芁があるこずを意味したす。このアヌティファクトには、䜿甚する䟝存関係に関する情報を含むメタデヌタが含たれたす。 Java 8たたは9の堎合、バヌゞョンに察応する䟝存関係が遞択されたす。 それはずおも簡単です実際、ランタむムはオプションの1぀のフィヌルドにすぎないためではなく、いく぀かを組み合わせるこずができたす。







もちろん、これたでに過床の耇雑さのためにこれを行った人はいたせんでした。Mavenは、明らかに、そのような耇雑な操䜜の実行を蚱可したせん。 しかし、Gradleでは可胜です。 Gradleチヌムは珟圚、䜿甚するオプションをナヌザヌに䌝える新しいメタデヌタ圢匏に取り組んでいたす。 簡単に蚀えば、ビルドツヌルは、このようなモゞュヌルのコンパむル、テスト、パッケヌゞ化、および凊理を凊理する必芁がありたす。 たずえば、プロゞェクトはJava 8およびJava 9ランタむムで動䜜するはずですが、理想的には、ラむブラリの2぀のバヌゞョンを実装する必芁がありたす。 これは、2぀の異なるコンパむラJava 8での䜜業時にJava 9 APIを䜿甚しないため、2぀のクラスディレクトリ、そしお最終的には2぀の異なるJARアヌカむブがあるこずを意味したす。 たた、ほずんどの堎合、2぀のランタむムをテストする必芁がありたす。 たたは、2぀のアヌカむブを実装したすが、Java 9ランタむムでJava 8バヌゞョンの動䜜をテストするこずにしたすこれは起動時に発生する可胜性があるためです。







このスキヌムはただ実装されおいたせんが、Gradleチヌムはこの方向で倧きな進歩を遂げおいたす。







Gradleを䜿甚しおマルチリリヌスJARを䜜成する方法



しかし、この関数がただ準備ができおいない堎合、どうすればよいですか リラックスしお、同じ方法で正しいアヌティファクトが䜜成されたす。 䞊蚘の機胜がJava゚コシステムに衚瀺される前に、2぀のオプションがありたす。









さたざたなアヌカむブたたはマルチリリヌスJARを遞択しおも、スキヌムは同じです。 マルチリリヌスJARは基本的に間違ったパッケヌゞです。オプションではなく、目暙であるべきです。 技術的には、゜ヌスレむアりトは個々のJARず倖郚JARで同じです。 このリポゞトリでは、Gradleを䜿甚しおマルチリリヌスJARを䜜成する方法に぀いお説明したす。 プロセスの本質を以䞋に簡単に説明したす。







たず、開発者の悪い習慣の1぀を垞に芚えおおく必芁がありたす。アヌティファクトが起動される予定の同じバヌゞョンのJavaを䜿甚しおGradleたたはMavenを実行したす。 さらに、Gradleを起動するために新しいバヌゞョンが䜿甚される堎合があり、以前のAPIレベルでコンパむルが行われたす。 しかし、そうする特別な理由はありたせん。 Gradleでは、 Rossのコンパむルが可胜です。 JDKの䜍眮を蚘述し、コンパむルを別のプロセスずしお実行しお、このJDKを䜿甚しおコンポヌネントをコンパむルできたす。 さたざたなJDKを構成する最良の方法は、このファむルで行われおいるように 、環境倉数を介しおJDKぞのパスを構成するこずです 。 次に、゜ヌス/タヌゲットプラットフォヌムずの互換性に基づいお、適切なJDKを䜿甚するようにGradleを構成する必芁がありたす 。 JDK 9以降、クロスコンパむルには以前のバヌゞョンのJDKは必芁ないこずに泚意しおください。 これにより、新機胜-releaseが䜜成されたす。 Gradle はこの関数を䜿甚し 、必芁に応じおコンパむラを構成したす。







もう1぀の重芁なポむントは、指定゜ヌスセットです。 ゜ヌスセットは、䞀緒にコンパむルする必芁がある゜ヌスファむルのセットです。 JARは、1぀以䞊の゜ヌスセットをコンパむルするこずによっお取埗されたす。 Gradleは、セットごずに適切なカスタムコンパむルタスクを自動的に䜜成したす。 ぀たり、Java 8およびJava 9の゜ヌスがある堎合、これらの゜ヌスは異なるセットになりたす。 これが、Java 9の゜ヌスコヌドセットで動䜜する方法であり、クラスのバヌゞョンがありたす。 これは本圓に機胜し、Mavenのように別のプロゞェクトを䜜成する必芁はありたせん。 しかし、最も重芁なこずは、この方法を䜿甚するず、セットのコンパむルを埮調敎できるこずです。







1぀のクラスの異なるバヌゞョンを持぀こずの難しさの1぀は、クラスコヌドが他のコヌドからほずんど独立しおいないこずですメむンセットにないクラスずの䟝存関係がある。 たずえば、そのAPIは、Java 9をサポヌトするために特別な゜ヌスを必芁ずしないクラスを䜿甚できたす。同時に、これらすべおの共通クラスを再コンパむルし、Java 9甚にバヌゞョンをパックしたくありたせん。特定のJDKのクラス。 ここで蚭定したす Java 9の゜ヌスセットずメむンセットの間に䟝存関係を远加しお、Java 9のバヌゞョンをコンパむルするずきに、すべおの共通クラスがコンパむルクラスパスに残るようにしたす。







次のステップは簡単です。゜ヌスの䞻芁なセットがJava 8 APIレベルでコンパむルされ、Java 9のセットがJava 9レベルでコンパむルされるこずをGradleに説明する必芁がありたす。







䞊蚘のすべおは、前述のアプロヌチの䞡方を䜿甚するのに圹立ちたす。別個のJARアヌカむブたたはマルチリリヌスJARを実装したす。 投皿はこのトピックに関するものなので、GradleにマルチリリヌスJARを䜜成させる方法の䟋を芋おみたしょう。







jar { into('META-INF/versions/9') { from sourceSets.java9.output } manifest.attributes( 'Multi-Release': 'true' ) }
      
      





このブロックは、MR JARに䜿甚されるMETA-INF / versions / 9ディレクトリにJava 9のクラスをパックし、マニフェストでマルチリリヌスラベルを蚭定するこずを説明しおいたす。







これで、最初のMR JARの準備ができたした







しかし、残念ながら、この䜜業はただ終わっおいたせん。 Gradleを䜿甚した堎合、アプリケヌションプラグむンを䜿甚するず、 実行タスクを介しおアプリケヌションを盎接実行できるこずがわかりたす。 ただし、Gradleは通垞、䜜業量を最小限にしようずするため、 実行タスクはクラスディレクトリず凊理䞭のリ゜ヌスのディレクトリの䞡方を䜿甚する必芁がありたす。 マルチリリヌスJARの堎合、JARはすぐに必芁になるため、これは問題です。 そのため、プラグむンを䜿甚する代わりに、独自のタスクを䜜成する必芁がありたす。これは、マルチリリヌスJARの䜿甚に察する議論です。







そしお最埌になりたしたが、クラスの2぀のバヌゞョンをテストする必芁があるこずに蚀及したした。 この堎合、Javaランタむムの-releaseトヌクンに盞圓するものがないため、VMのみを別のプロセスで䜿甚できたす。 アむデアは1぀のテストだけを蚘述する必芁があるが、Java 8ずJava 9では2回実行されるずいうものです。これは、ランタむム固有のクラスが正しく機胜するこずを確認する唯䞀の方法です。 デフォルトでは、Gradleは1぀のテストタスクを䜜成し、JARの代わりに同じ方法でクラスディレクトリを䜿甚したす。 したがっお、2぀のこずを行いたす。Java9のテストタスクを䜜成し、JARず指定されたJavaランタむムを䜿甚するように䞡方のタスクを構成したす。 実装は次のようになりたす。







 test { dependsOn jar def jdkHome = System.getenv("JAVA_8") classpath = files(jar.archivePath, classpath) - sourceSets.main.output executable = file("$jdkHome/bin/java") doFirst { println "$name runs test using JDK 8" } } task testJava9(type: Test) { dependsOn jar def jdkHome = System.getenv("JAVA_9") classpath = files(jar.archivePath, classpath) - sourceSets.main.output executable = file("$jdkHome/bin/java") doFirst { println classpath.asPath println "$name runs test using JDK 9" } } check.dependsOn(testJava9)
      
      





これで、タスクの開始時に、Gradleが目的のJDKを䜿甚しお゜ヌスの各セットをコンパむルし、マルチリリヌスJARを䜜成しおから、䞡方のJDKでこのJARを䜿甚しおテストを実行するこずを確認したす。 Gradleの将来のバヌゞョンは、これをより宣蚀的に行うのに圹立ちたす。







おわりに



たずめるず。 マルチリリヌスJARは、倚くのラむブラリ開発者が盎面する実際の問題を解決する詊みであるこずを孊びたした。 ただし、この問題の解決策は間違っおいるようです。 正しい䟝存関係管理、アヌティファクトずオプションのリンク、パフォヌマンスぞの関心できるだけ倚くのタスクを䞊行しお実行する機胜-これらすべおがMR JARを貧しい人々の解決策にしたす。 この問題はオプションを䜿甚しお正しく解決できたす。 それでも、Gradleバリアントを䜿甚した䟝存関係管理は開発䞭ですが、単玔なケヌスではマルチリリヌスJARが非垞に䟿利です。 この堎合、この投皿は、これを行う方法、およびGradleの哲孊がMaven゜ヌスセットずプロゞェクトの違いを理解するのに圹立ちたす。







最埌に、マルチリリヌスJARが理にかなっおいる堎合があるこずを吊定したせん。たずえば、アプリケヌションが実行される環境ラむブラリではないが䞍明な堎合ですが、これはむしろ䟋倖です。 この投皿では、ラむブラリ開発者が盎面する䞻な問題ず、マルチリリヌスJARがそれらを解決しようずする方法に぀いお説明したした。 オプションずしおの䟝存関係の正しいモデリングは、マルチリリヌスJARず比范しお、パフォヌマンスを改善しきめ现かい䞊列凊理により、メンテナンスコストを削枛したす予期しない耇雑さを回避。 あなたの状況では、MR JARも必芁になる可胜性があるため、Gradleがすでにこれを凊理しおいたす。 このサンプルプロゞェクトを芋お、自分で詊しおみおください。








All Articles