Может ли программа зависеть от библиотеки во время компиляции, но не во время выполнения?

Я понимаю разницу между временем выполнения и временем компиляции и как различать их, но я просто не вижу необходимости проводить различие между временем компиляции и временем выполнения зависимости.

Я задыхаюсь вот от чего: как может программа не зависит на что-то во время выполнения, что это зависит во время компиляции? Если мое приложение Java использует log4j, то ему нужен log4j.jar файл для компиляции (мой код интегрируется с и вызов методов-членов изнутри log4j), а также во время выполнения (мой код абсолютно не контролирует то, что происходит внутри кода log4j.jar РАН).

Я читаю о таких инструментах разрешения зависимостей, как Ivy и Maven, и эти инструменты четко делают различие между этими двумя типами зависимостей. Я просто не понимаю, зачем это нужно.

может ли кто-нибудь дать простое объяснение типа"King's English", предпочтительно с фактическим примером, что даже плохой sap как я мог понять?

9 ответов


зависимость времени компиляции обычно требуется во время выполнения. В maven, a compile зависимость области будет добавлена в путь к классам во время выполнения (например, в wars они будут скопированы в WEB-INF/lib).

это, однако, не является строго обязательным; например, мы можем компилировать против определенного API, делая его зависимостью времени компиляции, но затем во время выполнения включают реализацию, которая также включает API.

могут быть случаи, когда проект требует определенная зависимость для компиляции, но тогда соответствующий код фактически не нужен, Но они будут редкими.

С другой стороны, очень часто используются зависимости времени выполнения, которые не требуются во время компиляции. Например, если вы пишете приложение Java EE 6, вы компилируете с API Java EE 6, но во время выполнения можно использовать любой контейнер Java EE; именно этот контейнер обеспечивает реализацию.

зависимостей времени компиляции можно избежать, используя отображение. Например, драйвер JDBC может быть загружен с Class.forName и фактический загруженный класс настраивается через файл конфигурации.


каждая зависимость Maven имеет область, которая определяет, на каком пути к классам доступна эта зависимость.

при создании JAR для проекта зависимости не связываются с сгенерированным артефактом; они используются только для компиляции. (Однако вы все равно можете заставить maven включать зависимости во встроенную банку, см.:включая зависимости в банке с Maven)

при использовании Maven для создания файла WAR или EAR можно настроить Maven на связывайте зависимости с созданным артефактом,а также можно настроить его для исключения определенных зависимостей из файла WAR с помощью предоставленной области.

наиболее распространенная область -Компилировать Области - указывает, что зависимость доступна для проекта на пути к классам компиляции, компиляции и выполнения модульного теста и возможном пути к классам среды выполнения при выполнении приложения. В веб-приложении Java EE это означает, что зависимость копируется в развернутое приложение. В .jar-файл однако зависимости не будут включены в область компиляции..

Объем Выполнения указывает, что для проекта доступна зависимость от путей выполнения модульного теста и выполнения среды выполнения, но в отличие от compile scope это недоступно при компиляции приложения или его модульные тесты. зависимость времени выполнения копируется в развернутое приложение, но недоступна во время компиляция! это хорошо для того, чтобы убедиться, что вы не зависите по ошибке от конкретной библиотеки. (См., например: http://www.tugay.biz/2016/12/apache-commons-logging-log4j-maven.html)

наконец, Предусматривает Возможность указывает, что контейнер, в котором выполняется приложение, предоставляет зависимость от вашего имени. В приложении Java EE это означает, что зависимость уже находится на пути к классам контейнера сервлета или сервера приложений и не копируется в развернутом приложении. это также означает, что вам нужна эта зависимость для компиляции вашего проекта.


вам нужны зависимости во время компиляции, которые могут понадобиться во время выполнения. Однако многие библиотеки работают без всех возможных зависимостей. т. е. библиотеки, которые могут использовать четыре разные библиотеки XML, но для работы требуется только одна.

многие библиотеки, нужны другие библиотеки, в свою очередь. Эти библиотеки не нужны во время компиляции, но необходимы во время выполнения. т. е. когда код фактически запущен.


Как правило, вы правы, и, вероятно, это идеальная ситуация, если зависимости времени выполнения и компиляции идентичны.

Я приведу вам 2 примера, когда это правило неверно.

Если класс A зависит от класса B, который зависит от класса C, который зависит от класса D, где A-ваш класс и B, C и D-классы из разных сторонних библиотек, вам нужно только B и C во время компиляции, и Вам также нужно D во время выполнения. Часто программы используют динамическую загрузку классов. В в этом случае вам не нужны классы динамически загружаемых библиотек, которые вы используете во время компиляции. Кроме того, часто библиотека выбирает, какую реализацию использовать во время выполнения. Например, ведение журнала SLF4J или Commons может изменить целевую реализацию журнала во время выполнения. Вам нужно только SSL4J во время компиляции.

противоположный пример, когда вам нужно больше зависимостей во время компиляции, чем во время выполнения. Думаю, что вы разрабатываете приложение, которое должно работать в разных средах, либо операционная система. Во время компиляции вам нужны все библиотеки платформы и только библиотеки, необходимые для текущей среды во время выполнения.

надеюсь, мои объяснения помогут.


обычно граф статических зависимостей является подграфом динамического, см., например,эта запись в блоге От автора NDepend.

тем не менее, есть некоторые исключения, в основном зависимости, которые добавляют поддержку компилятора, которая становится невидимой во время выполнения. Например, для генерации кода как через Ломбок или дополнительные проверки через (подключаемый тип-)Checker Framework.


просто столкнулся с проблемой, которая отвечает на ваш вопрос. servlet-api.jar является временной зависимостью в моем веб-проекте и требуется как во время компиляции, так и во время выполнения. Но!--0--> также входит в мою библиотеку Tomcat.

решение здесь-сделать servlet-api.jar в maven доступен только во время компиляции и не упакован в мой файл war, чтобы он не конфликтовал с servlet-api.jar содержится в моей библиотеке Tomcat.

Я надеюсь, что это объясняет время компиляции и время выполнения зависимость.


я понимаю разницу между временем выполнения и временем компиляции и как чтобы отличить одно от другого, но я просто не вижу необходимости сделайте различие между зависимостями времени компиляции и времени выполнения.

общие концепции времени компиляции и выполнения и Maven specific compile и runtime зависимости объем-это две очень разные вещи. Вы не можете напрямую сравнить их, поскольку они не имеют одного и того же фрейма : общая компиляция и понятия времени выполнения широки, в то время как maven compile и runtime понятия области - это, в частности, доступность/видимость зависимостей в зависимости от времени : компиляция или выполнение.
Не забывайте, что Maven-это прежде всего a javac/java wrapper и что в Java у вас есть путь к классам времени компиляции, который вы указываете с javac -cp ... и путь к классам среды выполнения, который вы указываете с помощью java -cp ... .
Было бы не неправильно считать Maven compile scope как способ добавить зависимость как в Java компиляция и выполнения classppath (javac и java) в то время как Maven runtime область можно рассматривать как способ добавить зависимость только в Java runtime classppath (javac).

я задыхаюсь от этого: как программа может не зависеть от чего-то во время выполнения, от которого это зависело во время компиляции?

то, что вы описываете, не имеет никакого отношения к runtime и compile объем.
Похоже больше к provided объем, который вы укажете в зависимости от этого во время компиляции, а не во время выполнения.
Вы используете его, поскольку вам нужна зависимость для компиляции, но вы не хотите включать его в упакованный компонент (JAR, WAR или любые другие), потому что зависимость уже предоставил средой: он может быть включен в сервер или любой путь пути к классам, указанный при запуске приложения Java.

если мое приложение Java использует log4j, тогда ему нужно log4j.jar файл для компиляции (мой код интеграция с методами-членами и вызов методов-членов изнутри log4j) как а также время выполнения (мой код абсолютно не контролирует то, что происходит как только код внутри log4j.jar РАН).

в этом случае да. Но предположим, что вам нужно написать портативный код, который полагается на slf4j как фасад перед log4j, чтобы иметь возможность переключиться на другую реализацию ведения журнала позже (log4J 2, logback или любой другие.)
В этом случае в вас 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 time, ничто не помешает вам ссылаться на классы 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. Чтобы написать переносимый код, вы не хотите, чтобы клиентский код мог ссылаться на классы конкретной зависимости СУБД (например : PostgreSQL JDBC dependency), но вы все равно хотите включить его в свое приложение, поскольку во время выполнения классы необходимы, чтобы API JDBC работал с этой СУБД.


во время компиляции вы включаете контракты/api, которые вы ожидаете от своих зависимостей. (например: здесь вы просто подписываете контракт с широкополосным интернет-провайдером) Во время выполнения фактически вы используете зависимости. (например: здесь вы на самом деле используете широкополосный интернет)


ответить на вопрос "как программа может не зависеть от чего-то во время выполнения, от чего она зависела во время компиляции?", давайте рассмотрим пример процессора аннотаций.

Предположим, вы написали свой собственный процессор аннотаций и предположим, что он имеет зависимость от времени компиляции отcom.google.auto.service:auto-service Так что он может использовать @AutoService. Эта зависимость требуется только для компиляции обработчика аннотаций, но она не требуется во время выполнения: все остальные проекты в зависимости от вашей аннотации процессор для обработки аннотаций делать не требуется зависимость от com.google.auto.service:auto-service во время работы (не во время компиляции, ни в любое другое время).

Это не очень распространено, но случается.