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


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

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

Я читаю об инструментах разрешения зависимостей, таких как Ivy и Maven, и эти инструменты четко различают эти два типа зависимостей. Я просто не понимаю, зачем это нужно. Может ли кто-нибудь дать простое объяснение типа "King's English", предпочтительно с реальным примером, который мог бы понять даже такой бедный болван, как я?
9 98

9 ответов:

Зависимость времени компиляции обычно требуется во время выполнения. В maven зависимость compile с областью действия будет добавлена в classpath во время выполнения (например, в 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, используя предоставленную область.

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

Область выполнения указывает, что для вашего проекта доступна зависимость от путей классов выполнения модульного теста и выполнения среды выполнения, но в отличие от области компиляции она недоступна при компиляции приложения или его модульных тестов. зависимость времени выполнения копируется в развернутое приложение, но она недоступна во время компиляции! это хорошо для того, чтобы убедиться, что вы этого не делаете. ошибочно зависят от конкретной библиотеки. (См., например: 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 Logging могут изменить целевую реализацию журнала во время выполнения. Вам нужен только сам SSL4J во время компиляции.

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

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

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

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

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

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

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

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

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

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

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

Если мое Java-приложение использует log4j, то ему требуется log4j.jar файл для компиляции (мой код интеграция с и вызов методы членов изнутри log4j) как ну и runtime (мой код не имеет абсолютно никакого контроля над тем, что происходит как только код внутри 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, ничто не помешает вам ссылаться на классы log4j в скомпилированном коде, и вы можете создать нежелательную связь с реализацией logging:

<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 dependence), но вы все равно хотите включить его в свое приложение, поскольку во время выполнения классы необходимы для того, чтобы API JDBC работал с этой СУБД.

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

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

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