Может ли программа зависеть от библиотеки во время компиляции, но не во время выполнения?
Я понимаю разницу между временем выполнения и временем компиляции и как различать их, но я просто не вижу необходимости делать различие между временем компиляции и временем выполнения зависимости.
Я задыхаюсь от этого: как может программа не зависит на что-то во время выполнения, что это зависит во время компиляции? Если мой Java приложение использует настройки log4j, то он должен файла log4j.jar для того, чтобы скомпилировать (мой код и интеграция с вызов методов-членов изнутри log4j) , а также во время выполнения (мой код не имеет абсолютно никакого контроля над тем, что происходит один раз код внутри log4j.jar is ran).
Я читаю о таких инструментах разрешения зависимостей, как Ivy и Maven, и эти инструменты четко различают эти два типа зависимостей. Я просто не понимаю, зачем это нужно.
может ли кто-нибудь дать простое объяснение типа "King's English", предпочтительно с фактическим примером, что даже плохой sap как я мог понять?
9 ответов:
во время выполнения обычно требуется зависимость времени компиляции. В maven, a
compile
зависимость от области действия будет добавлена в classpath во время выполнения (например, в wars они будут скопированы в WEB-INF/lib).это, однако, не является строго обязательным; например, мы можем компилировать против определенного API, что делает его зависимостью времени компиляции, но затем во время выполнения включить реализацию, которая также включает API.
могут быть крайние случаи, когда проект требует определенная зависимость для компиляции, но тогда соответствующий код на самом деле не нужен, Но они будут редкими.
С другой стороны, включение зависимостей времени выполнения, которые не нужны во время компиляции, очень распространено. Например, если вы пишете приложение Java EE 6, вы компилируете с API Java EE 6, но во время выполнения можно использовать любой контейнер Java EE; именно этот контейнер обеспечивает реализацию.
зависимостей времени компиляции можно избежать с помощью отображение. Например, драйвер JDBC может быть загружен с
Class.forName
и фактический загруженный класс можно настроить с помощью файла конфигурации.
каждая зависимость Maven имеет область, которая определяет, на каком пути к классу эта зависимость доступна.
при создании JAR для проекта зависимости не связываются с созданным артефактом; они используются только для компиляции. (Однако вы все равно можете заставить maven включать зависимости в встроенный jar, см.: включая зависимости в банке с 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 может изменить целевую реализацию журнала во время выполнения. Вам нужно только SSL4J сам во время компиляции.
противоположный пример, когда вам нужно больше зависимостей во время компиляции, чем во время выполнения. Думаю, что вы разрабатываете приложение, которое должно работать в разных средах, либо операционная система. Вам нужны все библиотеки платформы во время компиляции и только библиотеки, необходимые для текущей среды во время выполнения.
Я надеюсь, что мои объяснения помогут.
обычно граф статических зависимостей является подграфом динамического, см. например эта запись в блоге От автора NDepend.
тем не менее, есть некоторые исключения, в основном зависимости, которые добавляют поддержку компилятора, которая становится невидимой во время выполнения. Например, для генерации кода как через Ломбок или дополнительные проверки через (pluggable type-)checker Framework.
просто столкнулся с проблемой, которая отвечает на ваш вопрос.
servlet-api.jar
является временной зависимостью в моем веб-проекте и необходим как во время компиляции, так и во время выполнения. Ноservlet-api.jar
также входит в мою библиотеку Tomcat.решение здесь-сделать
servlet-api.jar
в maven доступно только во время компиляции и не упаковано в мой файл war, чтобы он не конфликтовал сservlet-api.jar
содержится в моей библиотеке Tomcat.Я надеюсь, что это объясняет время компиляции и выполнения зависимость.
я понимаю разницу между временем выполнения и компиляции и как чтобы различать их, но я просто не вижу необходимости сделайте различие между зависимостями времени компиляции и времени выполнения.
общие понятия времени компиляции и времени выполнения и Maven specific
compile
иruntime
зависимости объем-это две очень разные вещи. Вы не можете напрямую сравнить их, поскольку они не имеют одного и того же фрейма : общая компиляция и понятия времени выполнения широки, в то время как mavencompile
иruntime
концепция области - это конкретно доступность/видимость зависимостей в зависимости от времени : компиляция или выполнение.
Не забывайте, что Maven-это прежде всего ajavac
/java
оболочка и что в Java у вас есть путь к классу времени компиляции, который вы указываете с помощьюjavac -cp ...
и путь к классу среды выполнения, который вы указываете с помощьюjava -cp ...
.
Было бы неплохо рассмотреть Mavencompile
область как способ добавить a зависимость как в компиляции Java, так и во время выполнения classppath (javac
иjava
) в то время как Mavenruntime
scope можно рассматривать как способ добавить зависимость только в Java runtime classppath (javac
).я задыхаюсь от этого: как программа может не зависеть от чего-то во время выполнения, от чего это зависит во время компиляции?
то, что вы описываете, не имеет никакого отношения к
runtime
иcompile
объем.
Похоже больше кprovided
объем, который вы укажете в зависимости от этого во время компиляции, а не во время выполнения.
Вы используете его, поскольку вам нужна зависимость для компиляции, но вы не хотите включать его в упакованный компонент (JAR, WAR или любые другие), потому что зависимость уже предоставил по среде: он может быть включен в сервер или любой путь пути к классу, указанный при запуске приложения Java.если мое приложение Java использует log4j, после этого для этого нужно log4j.jar файл для компиляции (мой код интеграция с и вызов методов-членов изнутри log4j) как ну и время выполнения (мой код не имеет абсолютно никакого контроля над тем, что происходит как только код внутри log4j.jar is ran).
в данном случае да. Но предположим, что вам нужно написать переносимый код, который полагается на 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 в скомпилированном коде и вы может так создать нежелательную связь с реализацией ведения журнала:<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
во время работы (не во время компиляции, ни в любое другое время).Это не очень распространено, но случается.