Scope maven что это
Scope maven что это
Introduction to the Dependency Mechanism
Dependency management is a core feature of Maven. Managing dependencies for a single project is easy. Managing dependencies for multi-module projects and applications that consist of hundreds of modules is possible. Maven helps a great deal in defining, creating, and maintaining reproducible builds with well-defined classpaths and library versions.
Transitive Dependencies
Maven avoids the need to discover and specify the libraries that your own dependencies require by including transitive dependencies automatically.
This feature is facilitated by reading the project files of your dependencies from the remote repositories specified. In general, all dependencies of those projects are used in your project, as are any that the project inherits from its parents, or from its dependencies, and so on.
There is no limit to the number of levels that dependencies can be gathered from. A problem arises only if a cyclic dependency is discovered.
With transitive dependencies, the graph of included libraries can quickly grow quite large. For this reason, there are additional features that limit which dependencies are included:
Although transitive dependencies can implicitly include desired dependencies, it is a good practice to explicitly specify the dependencies your source code uses directly. This best practice proves its value especially when the dependencies of your project change their dependencies.
For example, assume that your project A specifies a dependency on another project B, and project B specifies a dependency on project C. If you are directly using components in project C, and you don’t specify project C in your project A, it may cause build failure when project B suddenly updates/removes its dependency on project C.
Another reason to directly specify dependencies is that it provides better documentation for your project: one can learn more information by just reading the POM file in your project, or by executing mvn dependency:tree.
Maven also provides dependency:analyze plugin goal for analyzing the dependencies: it helps making this best practice more achievable.
Dependency Scope
Dependency scope is used to limit the transitivity of a dependency and to determine when a dependency is included in a classpath.
There are 6 scopes:
Each of the scopes (except for import ) affects transitive dependencies in different ways, as is demonstrated in the table below. If a dependency is set to the scope in the left column, a transitive dependency of that dependency with the scope across the top row results in a dependency in the main project with the scope listed at the intersection. If no scope is listed, it means the dependency is omitted.
compile | provided | runtime | test | |
compile | compile(*) | — | runtime | — |
provided | provided | — | provided | — |
runtime | runtime | — | runtime | — |
test | test | — | test | — |
(*) Note: it is intended that this should be runtime scope instead, so that all compile dependencies must be explicitly listed. However, if a library you depend on extends a class from another library, both must be available at compile time. For this reason, compile time dependencies remain as compile scope even when they are transitive.
Dependency Management
The dependency management section is a mechanism for centralizing dependency information. When you have a set of projects that inherit from a common parent, it’s possible to put all information about the dependency in the common POM and have simpler references to the artifacts in the child POMs. The mechanism is best illustrated through some examples. Given these two POMs which extend the same parent:
These two example POMs share a common dependency and each has one non-trivial dependency. This information can be put in the parent POM like this:
Then the two child POMs become much simpler:
A second, and very important use of the dependency management section is to control the versions of artifacts used in transitive dependencies. As an example consider these projects:
When maven is run on project B, version 1.0 of artifacts a, b, c, and d will be used regardless of the version specified in their POM.
The reference information about the dependency management tags is available from the project descriptor reference.
Importing Dependencies
The examples in the previous section describe how to specify managed dependencies through inheritance. However, in larger projects it may be impossible to accomplish this since a project can only inherit from a single parent. To accommodate this, projects can import managed dependencies from other projects. This is accomplished by declaring a POM artifact as a dependency with a scope of «import».
Assuming A is the POM defined in the preceding example, the end result would be the same. All of A’s managed dependencies would be incorporated into B except for d since it is defined in this POM.
In the example above Z imports the managed dependencies from both X and Y. However, both X and Y contain dependency a. Here, version 1.1 of a would be used since X is declared first and a is not declared in Z’s dependencyManagement.
This process is recursive. For example, if X imports another POM, Q, when Z is processed it will simply appear that all of Q’s managed dependencies are defined in X.
Bill of Materials (BOM) POMs
Imports are most effective when used for defining a «library» of related artifacts that are generally part of a multiproject build. It is fairly common for one project to use one or more artifacts from these libraries. However, it has sometimes been difficult to keep the versions in the project using the artifacts in synch with the versions distributed in the library. The pattern below illustrates how a «bill of materials» (BOM) can be created for use by other projects.
The root of the project is the BOM POM. It defines the versions of all the artifacts that will be created in the library. Other projects that wish to use the library should import this POM into the dependencyManagement section of their POM.
The parent subproject has the BOM POM as its parent. It is a normal multiproject pom.
Next are the actual project POMs.
The project that follows shows how the library can now be used in another project without having to specify the dependent project’s versions.
Finally, when creating projects that import dependencies, beware of the following:
System Dependencies
Important note: This is deprecated.
Dependencies with the scope system are always available and are not looked up in repository. They are usually used to tell Maven about dependencies which are provided by the JDK or the VM. Thus, system dependencies are especially useful for resolving dependencies on artifacts which are now provided by the JDK, but were available as separate downloads earlier. Typical examples are the JDBC standard extensions or the Java Authentication and Authorization Service (JAAS).
Maven Scopes and Gradle Configurations Explained
One of the key features of a build tool for Java is dependency management. We declare that we want to use a certain third-party library in our own project and the build tool takes care of downloading it and adding it to the classpath at the right times in the build lifecycle.
Maven has been around as a build tool for a long time. It’s stable and still well liked in the Java community.
Gradle has emerged as an alternative to Maven quite some time ago, heavily relying on Maven dependency infrastructure, but providing a more flexible way to declare dependencies.
Whether you’re moving from Maven to Gradle or you’re just interested in the different ways of declaring dependencies in Maven or Gradle, this article will give an overview.
What’s a Scope / Configuration?
A Maven pom.xml file or a Gradle build.gradle file specifies the steps necessary to create a software artifact from our source code. This artifact can be a JAR file or a WAR file, for instance.
In most non-trivial projects, we rely on third-party libraries and frameworks. So, another task of build tools is to manage the dependencies to those third-party libraries and frameworks.
Say we want to use the SLF4J logging library in our code. In a Maven pom.xml file, we would declare the following dependency:
In a Gradle build.gradle file, the same dependency would look like this:
Both Maven and Gradle allow to define different groups of dependencies. These dependency groups are called “scopes” in Maven and “configurations” in Gradle.
Each of those dependency groups has different characteristics and answers the following questions differently:
In the above example, we added the SLF4J dependency to the Maven compile scope and the Gradle implementation configuration, which can be considered the defaults for Maven and Gradle, respectively.
Let’s look at the semantics of all those scopes and configurations.
Maven Scopes
Maven provides 6 scopes for Java projects.
We’re not going to look at the system and import scopes, however, since they are rather exotic.
compile
The compile scope is the default scope. We can use it when we have no special requirements for declaring a certain dependency.
Note that the compile scope leaks into the compile time, thus promoting dependency pollution.
provided
We can use the provided scope to declare a dependency that will not be included in the final build artifact.
If we rely on the Servlet API in our project, for instance, and we deploy to an application server that already provides the Servlet API, then we would add the dependency to the provided scope.
runtime
We use the runtime scope for dependencies that are not needed at compile time, like when we’re compiling against an API and only need the implementation of that API at runtime.
An example is SLF4J where we include slf4j-api to the compile scope and an implementation of that API (like slf4j-log4j12 or logback-classic ) to the runtime scope.
We can use the test scope for dependencies that are only needed in tests and that should not be available in production code.
Examples dependencies for this scope are testing frameworks like JUnit, Mockito, or AssertJ.
Gradle Configurations
Gradle has a more diverse set of configurations. This is the result of Gradle being younger and more actively developed, and thus able to adapt to more use cases.
Let’s look at the standard configurations of Gradle’s Java Library Plugin. Note that we have to declare the plugin in the build script to get access to the configurations:
implementation
The implementation configuration should be considered the default. We use it to declare dependencies that we don’t want to expose to our consumers’ compile time.
This configuration was introduced to replace the deprecated compile configuration to avoid polluting the consumer’s compile time with dependencies we actually don’t want to expose.
We use the api configuration do declare dependencies that are part of our API, i.e. for dependencies that we explicitly want to expose to our consumers.
This is the only standard configuration that exposes dependencies to the consumers’ compile time.
compileOnly
The compileOnly configuration allows us to declare dependencies that should only be available at compile time, but are not needed at runtime.
An example use case for this configuration is an annotation processor like Lombok, which modifies the bytecode at compile time. After compilation it’s not needed anymore, so the dependency is not available at runtime.
runtimeOnly
The runtimeOnly configuration allows us to declare dependencies that are not needed at compile time, but will be available at runtime, similar to Maven’s runtime scope.
An example is again SLF4J where we include slf4j-api to the implementation configuration and an implementation of that API (like slf4j-log4j12 or logback-classic ) to the runtimeOnly configuration.
testImplementation
We can use it for declaring dependencies to testing frameworks like JUnit or Mockito that we only need in tests and that should not be available in the production code.
testCompileOnly
I can’t think of a specific example, but there may be some annotation processors similar to Lombok that are only relevant for tests.
testRuntimeOnly
An example would be declaring a dependency to the JUnit Jupiter Engine, which runs our unit tests, but which we don’t compile against.
Combining Gradle Configurations
Since the Gradle configurations are very specific, sometimes we might want to combine their features. In this case, we can declare a dependency with more than one configuration. For example, if we want a compileOnly dependency to also be available at test compile time, we additionally declare it to the testCompileOnly configuration:
To remove the duplicate declaration, we could also tell Gradle that we want the testCompileOnly configuration to include everything from the compileOnly configuration:
Do this with care, however, since we’re losing flexibility in declaring dependencies every time we’re combining two configurations this way.
Maven Scopes vs. Gradle Configurations
Maven scopes don’t translate perfectly to Gradle configurations because Gradle configurations are more granular. However, here’s a table that translates between Maven scopes and Gradle configurations with a few notes about differences:
Maven Scope | Equivalent Gradle Configuration |
---|---|
compile | api if the dependency should be exposed to consumers, implementation if not |
provided | compileOnly (note that the provided Maven scope is also available at runtime while the compileOnly Gradle configuration is not) |
runtime | runtimeOnly |
test | testImplementation |
Conclusion
Gradle, being the younger build tool, provides a lot more flexibility in declaring dependencies. We have finer control about whether dependencies are available in tests, at runtime or at compile time.
Furthermore, with the api and implementation configurations, Gradle allows us to explicitly specify which dependencies we want to expose to our consumers, reducing dependency pollution to the consumers.
Follow me on Twitter for more tips on how to become a better software developer.
Tom Hombergs
As a professional software engineer, consultant, architect, and general problem solver, I’ve been practicing the software craft for more than ten years and I’m still learning something new every day. I love sharing the things I learned, so you (and future me) can get a head start.
Grow as a Software Engineer in Just 5 Minutes a Week
Join more than 4,000 software engineers who get a free weekly email with hacks to become more productive and grow as a software engineer. Also get 50% off my software architecture book, if you want.
Have a look at the previous newsletters to see what’s coming.
Maven Dependency Scopes
Last Updated: August 30, 2020
Maven dependency scope – compile
This is maven default scope. Dependencies with compile scope are needed to build, test, and run the project.
Scope compile is to be required in most of the cases to resolve the import statements into your java classes sourcecode.
Maven dependency scope – provided
Maven dependency scope provided is used during build and test the project. They are also required to run, but should not exported, because the dependency will be provided by the runtime, for instance, by servlet container or application server.
Maven dependency scope – runtime
Dependencies with maven dependency scope runtime are not needed to build, but are part of the classpath to test and run the project.
Maven dependency scope – test
Dependencies with maven dependency scope test are not needed to build and run the project. They are needed to compile and run the unit tests.
Maven dependency scope – system
Maven dependency scope – import
import scope is only supported on a dependency of type pom in the dependencyManagement section. It indicates the dependency to be replaced with the effective list of dependencies in the specified POM’s dependencyManagement section.
Maven dependency transitivity resolution
When you include a maven dependency and it has it’s own other dependencies (i.e. transitive dependencies) then you may want to be clear about the scope of these transitive dependencies as well.
Let’s understand about maven transitive dependencies with a simple table. In this table, if a dependency is set to the scope in the left column, transitive dependencies at top row will result in a dependency with the scope listed at their intersection.
Maven зависимости, dependency
Редко когда какой-либо проект обходится без дополнительных библиотек. Как правило, используемые в проекте библиотеки необходимо включить в сборку, если это не проект OSGi или WEB (хотя и для них зачастую приходится включать в проект отдельные библиотеки). Для решения данной задачи в maven-проекте необходимо использовать зависимость dependency, устанавливаемые в файле pom.xml, где для каждого используемого в проекте артефакта необходимо указать :
Параметры GAV
Значения идентификаторов groupId и artifactId подключаемых библиотек практически всегда можно найти на сайте www.mvnrepository.com. Если найти требуемую библиотеку в этом репозитории не удается, то можно использовать дополнительный репозиторий http://repo1.maven.org/maven2.
Cтруктура файла pom.xml и описание секции подключения к проекту репозитория представлены на главной странице фреймворка maven.
Классификатор classifier
Классификатор classifier используется в тех случаях, когда деление артефакта по версиям является недостаточным. К примеру, определенная библиотека (артефакт) может быть использована только с определенной JDK (VM), либо разработана под windows или linux. Определять этим библиотекам различные версии – идеологически не верно. Но вот использованием разных классификаторов можно решить данную проблему.
Значение classifier добавляется в конец наименования файла артефакта после его версии перед расширением. Для представленного выше примера полное наименование файла имеет следующий вид : json-lib-2.4-jdk15.jar.
Расположение артефакта в репозитории
В maven-мире «оперируют», как правило, артефактами. Это относится и к создаваемому разработчиком проекту. Когда выполняется сборка проекта, то формируется наименование файла, в котором присутствуют основные параметры GAV. После сборки этот артефакт готов к установке как в локальный репозиторий для использования в других проектах, так и для распространения в public-репозитории. Помните, что в начале файла pom.xml указываются параметры GAV артефакта :
Формально координата артефакта представляет четыре слова, разделенные знаком двоеточия в следующем порядке groupId:artifactId:packaging:version.
Полный путь, по которому находится файл артефакта в локальном репозитории, использует указанные выше четыре характеристики. В нашем примере для зависимости JSON это будет «HOME_PATH/.m2/repository/net/sf/json-lib/json-lib/2.4/json-lib-2.4-jdk15.jar». Параметру groupId соответствует директория (net/sf/json-lib) внутри репозитория (/.m2/repository). Затем идет поддиректория с artifactId (json-lib), внутри которой располагается поддиректория с версией (2.4). В последней располагается сам файл, в названии которого присутствуют все параметры GAV, а расширение файла соогласуется с параметром packaging.
Здесь следует заметить, что правило, при котором «расширение файла с артефактом соответствует его packaging» не всегда верно. К примеру, те, кто знаком с разработкой enterprise приложений, включающих бизнес-логику в виде ejb-модулей и интерфейса в виде war-модулей, знают, что модули ejb-внешне представляют собой обычный архивный файл с расширением jar, хотя в теге packaging определено значение ejb.
В каталоге артефакта, помимо самого файла, хранятся связанные с ним файлы с расширениями *.pom, *.sha1 и *.md5. Файл *.pom содержит полное описание сборки артефакта, а в файлах с расширениями sha1, md5 хранятся соответствующие значения MessageDidgest, полученные при загрузке артефакта в локальный репозиторий. Если исходный файл в ходе загрузки по открытым каналам Internet получил повреждения, то вычисленное значения sha1 и md5 будут отличаться от загруженного значения. А, следовательно, maven должен отвергнуть такой артефакт и попытаться загрузить его из другого репозитория.
Область действия зависимости, scope
Область действия scope определяет этап жизненного цикла проекта, в котором эта зависимость будет использоваться.
test
Если зависимость junit имеет область действия test, то эта зависимость будет использована maven’ом при выполнении компиляции той части проекта, которая содержит тесты, а также при запуске тестов на выполнение и построении отчета с результатами тестирования кода. Попытка сослаться на какой-либо класс или функцию библиотеки junit в основной части приложения (каталог src/main) вызовет ошибку.
compile
К наиболее часто используемой зависимости относится compile (используется по умолчанию). Т.е. dependency, помеченная как compile, или для которой не указано scope, будет доступна как для компиляции основного приложения и его тестов, так и на стадиях запуска основного приложения или тестов. Чтобы инициировать запуск тестов из управляемого maven-проекта можно выполнив команду «mvn test», а для запуска приложения используется плагин exec.
provided
Область действия зависимости provided аналогична compile, за исключением того, что артефакт используется на этапе компиляции и тестирования, а в сборку не включается. Предполагается, что среда исполнения (JDK или WEB-контейнер) предоставят данный артефакт во время выполнения программы. Наглядным примером подобных артефактов являются такие библиотеки, как hibernate или jsf, которые необходимы на этапе разработки приложения.
runtime
Область действия зависимости runtime не нужна для компиляции проекта и используется только на стадии выполнения приложения.
system
Область действия зависимости system аналогична provided за исключением того, что содержащий зависимость артефакт указывается явно в виде абсолютного пути к файлу, определенному в теге systemPath. Обычно к таким артефактам относятся собственные наработки, и искать их в центральном репозитории, куда Вы его не размещали, не имеет смысла :
Версия SNAPSHOT
При определении версии релиза можно использовать SNAPSHOT, который будет свидетельствовать о том, что данный артефакт находится в процессе разработки и в него вносятся постоянные изменения, например делается bugfixing или дорабатывается функционал. В этом случае код и функциональность артефакта последней сборки в репозитории могут не соответствовать реальному положению дел. Таким образом нужно четко отделять стабильные версии артефактов от не стабильных. Связываясь с нестабильными артефактами нужно быть готовыми к тому, что их поведение может измениться и наш проект, использующий такой артефакт, может вызывать исключения. Следовательно, нужно определиться с вопросом: нужно ли обновлять из репозитория артефакт, ведь его номер формально остался неизменным.
Если версия модуля определяется как SNAPSHOT (версия 2.0.0-SNAPSHOT), то maven будет либо пересобирать его каждый раз заново вместо того, чтобы подгружать из локального репозитория, либо каждый раз загружать из public-репозитория. Указывать версию как SNAPSHOT нужно в том случае, если проект в работе и всегда нужна самая последняя версия.
Транзитивные зависимости
Начиная со второй версии фреймворка maven были введены транзитивные зависимости, которые позволяет избегать необходимости изучения и определения библиотек, которые требуются для самой зависимости. Maven включает их автоматически. В общем случае, все зависимости, используемые в проекте, наследуются от родителей. Ограничений по уровню наследований не существует, что, в свою очередь, может вызвать их сильный рост. В качестве примера можно рассмотреть создание проекта «A», который зависит от проекта «B». Но проект «B», в свою очередь, зависит от проекта «C». Подобная цепочка зависимостей может быть сколь угодно длинной. Как в этом случае поступает maven и как связан проект «A» и c проектом «C».
В следующей табличке, позаимствованной с сайта maven, представлен набор правил переноса области scope. К примеру, если scope артефакта «B» compile, а он, в свою очередь, подключает библиотеку «C» как provided, то наш проект «A» будет зависеть от «C» так как указано в ячейке находящейся на пересечении строки «compile» и столбца «provided».
Compile | Provided | Runtime | Test | |
Compile | Compile | — | Runtime | — |
Provided | Provided | Provided | Provided | — |
Runtime | Runtime | — | Runtime | — |
Test | Test | Test | Test | — |
Плагин dependency
Имея приведенную выше таблицу правил переноса scope и набор соответствующих артефактам файлов pom можно построить дерево зависимостей для каждой из фаз жизненного цикла проекта. Строить вручную долго и сложно. Можно использовать maven-плагин dependency и выполнить команду «mvn dependency:list», в результате выполнения которой получим итоговый список артефактов и их scope :
Однако к такому списку могут возникнуть вопросы : откуда взялся тот или иной артефакт? Т.е. желательно показать транзитные зависимости. И вот, команда «mvn dependency:tree» позволяет сформировать такое дерево зависимостей :
Плагин dependency содержит большое количество целей goal, к наиболее полезным из которых относятся :
Копирование зависимости в локальный репозиторий
Следующий команда загрузит библиотеку JFreeChart (версия 1.0.19) в локальный репозиторий.