Может ли программа зависеть от библиотеки во время компиляции, но не от времени выполнения? - PullRequest
103 голосов
/ 16 августа 2011

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

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

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

Может ли кто-нибудь дать простое объяснение типа «корольский английский», желательно с реальным примером, который мог бы понять даже такой бедный сок, как я?

Ответы [ 9 ]

60 голосов
/ 16 августа 2011

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

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

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

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

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

26 голосов
/ 26 сентября 2014

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

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

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

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

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

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

9 голосов
/ 16 августа 2011

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

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

4 голосов
/ 16 августа 2011

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

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

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

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

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

3 голосов
/ 06 сентября 2011

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

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

2 голосов
/ 15 августа 2012

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

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

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

1 голос
/ 30 августа 2018

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

Общие понятия времени компиляции и времени выполнения и специфичные для 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 scope.
Похоже, больше к области provided, которую вы указываете для зависимости, зависит отэто во время компиляции, но не во время выполнения.
Вы используете это, поскольку вам нужна зависимость для компиляции, но вы не хотите включать ее в упакованный компонент (JAR, WAR или любой другой), потому что зависимость уже предоставляется средой: он может быть включен в сервер или в любой путь пути к классу, указанный при запуске приложения 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 работает с этой СУБД.

0 голосов
/ 31 марта 2018

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

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

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

0 голосов
/ 16 августа 2011

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

...