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

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

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

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

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

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

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

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

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

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

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

Ответ 8

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

Ответ 9

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

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

Это не очень часто, но так бывает.