web-dev-qa-db-ja.com

プログラムはコンパイル中にライブラリに依存しますが、ランタイムには依存できませんか?

ランタイムとコンパイル時の違いと、この2つを区別する方法は理解していますが、コンパイル時とランタイム依存関係を区別する必要はありません。

私が窒息しているのはこれです:どのようにプログラムがコンパイル時に依存していた実行時に何かに依存しないことができますか? my Java appがlog4jを使用する場合、コンパイル(log4j内部からの統合とメンバーメソッドの呼び出しを行うコード)およびランタイム(私のコードには絶対にあります)するために、log4j.jarファイルlog4j.jar内のコードが実行されると何が起こるかを制御できません。

私はIvyやMavenなどの依存関係解決ツールを読んでいますが、これらのツールはこれら2つのタイプの依存関係を明確に区別しています。私はそれの必要性を理解していません。

誰でも簡単な「王の英語」タイプの説明を、できれば私のような貧しい樹液でも理解できる実際の例で説明できますか?

104
IAmYourFaja

通常、実行時にはコンパイル時の依存関係が必要です。 Mavenでは、compileスコープの依存関係が実行時にクラスパスに追加されます(たとえば、warsではWEB-INF/libにコピーされます)。

ただし、厳密に必須ではありません。たとえば、特定のAPIに対してコンパイルし、それをコンパイル時の依存関係にしますが、実行時にAPIも含む実装を含めます。

プロジェクトがコンパイルするために特定の依存関係を必要とするが、対応するコードが実際に必要ではないという例外的なケースがあるかもしれませんが、これらはまれです。

一方、コンパイル時に不要なランタイム依存関係を含めることは非常に一般的です。たとえば、Java EE 6アプリケーションを作成している場合、Java EE 6 APIに対してコンパイルしますが、実行時には、任意のJava EEコンテナを使用できます。実装を提供するのはこのコンテナです。

リフレクションを使用すると、コンパイル時の依存関係を回避できます。たとえば、JDBCドライバーは Class.forName そしてロードされる実際のクラスは、構成ファイルを介して構成可能です。

60
Artefacto

各Maven依存関係には、その依存関係を使用できるクラスパスを定義するスコープがあります。

プロジェクトのJARを作成するとき、依存関係は生成されたアーティファクトにバンドルされません。これらはコンパイルにのみ使用されます。 (ただし、mavenにビルドされたjarに依存関係を含めることもできます。参照: Mavenを含むjarに依存関係を含める

Mavenを使用してWARまたはEARファイルを作成する場合、生成されたアーティファクトに依存関係をバンドルするようにMavenを構成できます。また、提供されたスコープを使用してWARファイルから特定の依存関係を除外するように構成できます。

最も一般的なスコープ—Compile Scope—は、コンパイルクラスパス、ユニットテストコンパイルおよび実行クラスパス、および実行時の最終的なランタイムクラスパスでプロジェクトに依存性が利用できることを示します。あなたの申請。 Java EE Webアプリケーションでは、依存関係はデプロイされたアプリケーションにコピーされます。ただし、.jarファイルでは、依存関係はコンパイルスコープに含まれません。

Runtime Scopeは、ユニットテスト実行およびランタイム実行クラスパスでプロジェクトに依存関係が利用可能であることを示しますが、コンパイルスコープとは異なり、アプリケーションをコンパイルしますまたはその単体テスト。 ランタイム依存関係はデプロイされたアプリケーションにコピーされますが、コンパイル中は利用できません!これは、特定のライブラリに誤って依存しないようにするのに適しています。 (例: http://www.tugay.biz/2016/12/Apache-commons-logging-log4j-maven.html

最後に、Provided Scopeは、アプリケーションが実行されるコンテナがユーザーに代わって依存関係を提供することを示します。 Java EEアプリケーションでは、これは依存関係がすでにサーブレットコンテナまたはアプリケーションサーバーのクラスパスにあり、はデプロイされたアプリケーションにコピーされないことを意味します。また、プロジェクトをコンパイルするためにこの依存関係が必要であることも意味します。

27
Koray Tugay

コンパイル時に、実行時に必要になる可能性のある依存関係が必要です。ただし、多くのライブラリは、可能なすべての依存関係なしで実行されます。つまり、4つの異なるXMLライブラリを使用できますが、動作するのに必要なライブラリは1つだけです。

多くのライブラリは、他のライブラリを順番に必要とします。これらのライブラリはコンパイル時には必要ありませんが、実行時には必要です。すなわち、コードが実際に実行されるとき。

9
Peter Lawrey

一般的には正しいです。実行時とコンパイル時の依存関係が同一であれば理想的です。

このルールが正しくない場合、2つの例を示します。

クラスAがクラスDに依存するクラスCに依存するクラスBに依存し、Aがクラスであり、B、CおよびDが異なるサードパーティライブラリのクラスである場合、コンパイル時にBとCのみが必要であり、Dも必要ですランタイム。多くの場合、プログラムは動的クラスロードを使用します。この場合、コンパイル時に使用しているライブラリによって動的にロードされるクラスは必要ありません。さらに、多くの場合、ライブラリは実行時に使用する実装を選択します。たとえば、SLF4JまたはCommons Loggingは、実行時にターゲットログの実装を変更できます。コンパイル時にSSL4J自体のみが必要です。

実行時よりもコンパイル時に多くの依存関係が必要な場合の例の反対。さまざまな環境またはオペレーティングシステムで動作する必要があるアプリケーションを開発していると考えてください。コンパイル時にはすべてのプラットフォーム固有のライブラリが必要であり、実行時には現在の環境に必要なライブラリのみが必要です。

私の説明が役立つことを願っています。

4
AlexR

通常、静的な依存関係グラフは、動的な依存関係グラフのサブグラフです。 NDependの作成者によるこのブログエントリ

とは言っても、いくつかの例外があります。主にコンパイラのサポートを追加する依存関係であり、実行時には見えなくなります。 Lombok を介したコード生成、または (pluggable type-)Checker Framework を介した追加チェックなど。

3
DaveFar

あなたの質問に答える問題に遭遇しました。 servlet-api.jarはWebプロジェクトの一時的な依存関係であり、コンパイル時と実行時の両方で必要です。ただし、servlet-api.jarもTomcatライブラリに含まれています。

ここでの解決策は、mavenのservlet-api.jarをコンパイル時にのみ使用可能にし、私のTomcatファイルに含まれるservlet-api.jarと衝突しないようにwarファイルにパッケージ化しないことです。

これがコンパイル時間とランタイム依存性を説明することを望みます。

2
Mayoor

実行時とコンパイル時の違いと、この2つを区別する方法は理解していますが、コンパイル時と実行時の依存関係を区別する必要はありません。

一般的なコンパイル時および実行時の概念とMaven固有のcompileおよびruntimeスコープの依存関係は、2つの非常に異なるものです。これらは同じフレームを持たないため、直接比較することはできません。一般的なコンパイルとランタイムの概念は幅広く、maven compileruntimeスコープの概念は依存性の可用性/可視性に関するものです時間に:コンパイルまたは実行。
Mavenがすべてjavac/Javaラッパーであり、Javaにコンパイル時のクラスパスがあることを忘れないでください。 javac -cp ...で指定し、Java -cp ...で指定するランタイムクラスパスを指定します。
Maven compileスコープを、Javaコンパイルおよびランタイムclassppath(javacおよびJava)Maven runtimeスコープは、Java runtime classppath(javac)。

私が窒息しているのはこれです:プログラムはコンパイル時に依存していた実行時に何かに依存しないのですか?

説明するものは、runtimeおよびcompileスコープとは関係ありません。
依存関係に指定するprovidedスコープは、実行時ではなくコンパイル時の依存性に依存するように見えます。
コンパイルに依存関係が必要なため使用しますが、依存関係は既にprovided byであるため、パッケージ化されたコンポーネント(JAR、WARまたはその他)に含めることは望ましくありません環境:Javaアプリケーションが開始されているとして指定されたサーバーまたはクラスパスの任意のパスに含めることができます。

My Java appがlog4jを使用している場合、log4j内でメンバーメソッドを統合および呼び出す私のコード)とランタイム(私のコードは絶対に持っています)をコンパイルするには、log4j.jarファイルが必要ですlog4j.jar内のコードが実行されると何が起こるかを制御できません。

この場合、はい。ただし、後で別のロギング実装(log4J 2、logbackまたはその他)に切り替えることができるように、log4jの前のファサードとしてslf4jに依存する移植可能なコードを記述する必要があるとします。
この場合、pomでslf4jをcompile依存関係として指定する必要があります(デフォルトです)が、log4j依存関係はruntime依存関係として指定します。

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>...</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>...</version>
    <scope>runtime</scope>
</dependency>

この方法では、コンパイルされたコードでlog4jクラスを参照できませんでしたが、slf4jクラスを参照することはできます。
compile時間で2つの依存関係を指定した場合、コンパイルされたコードでlog4jクラスを参照することを妨げるものは何もないので、ロギング実装との望ましくない結合を作成できます。

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>...</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>...</version>
</dependency>

runtimeスコープの一般的な使用法は、JDBC依存関係宣言です。移植可能なコードを記述するために、クライアントコードが特定のDBMS依存関係のクラス(たとえば、PostgreSQL JDBC依存関係)を参照することは望ましくありませんが、実行時にクラスを作成するのに必要なのと同じようにアプリケーションに含める必要がありますJDBC APIはこのDBMSで動作します。

1
davidxxx

コンパイル時に、依存関係から期待されるコントラクト/ APIを有効にします。 (例:ここでは、ブロードバンドインターネットプロバイダーとの契約に署名するだけです)実際には、実行時に依存関係を使用しています。 (例:ここでは、実際にブロードバンドインターネットを使用しています)

0
Nicolae Dascalu

「プログラムは、コンパイル時に依存していた実行時の何かにどのように依存しないのでしょうか?」という質問に答えるために、注釈プロセッサの例を見てみましょう。

独自の注釈プロセッサを作成し、コンパイル時の依存関係がoncom.google.auto.service:auto-serviceを使用できるように@AutoService。この依存関係は、注釈プロセッサをコンパイルするためにのみ必要ですが、実行時には必要ありません。注釈を処理するための注釈プロセッサに依存する他のすべてのプロジェクトは、notcom.google.auto.service:auto-service実行時(コンパイル時でも、その他の時でも)。

これはあまり一般的ではありませんが、起こります。

0
user23288