Избранное сообщение

Фетісов В. С. Комп’ютерні технології в тестуванні. Навчально-методичний посібник. 2-ге видання, перероблене та доповнене / Мои публикации

В 10-х годах я принимал участие в программе Европейского Союза Tempus "Освітні вимірювання, адаптовані до стандартів ЄС". В рамк...

Благодаря Интернету количество писателей и поэтов увеличивается в геометрической прогрессии. Поголовье читателей начинает заметно отставать.

пятница, 3 марта 2017 г.

Как с помощью maven работать с библиотеками, которых в maven нет / Программирование на Java

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

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

Эта статья для тех, кто только начинает осваивать java.

image

В моей предыдущей статье было сказано, что maven сам скачает все указанные в pom.xml зависимости. А вот что будет, если он какую-нибудь зависимость не найдёт? В таком случае maven скажет, что зависимость не обнаружена и прервёт процесс сборки с ошибкой. Что делать в этом случае?

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

Зависимость может быть в интернете в каком-то месте, о существовании которого maven не знает. Ещё она может быть в виде jar файла у вас на руках и, наконец, в виде исходного кода, оформленного как maven проект.

Об этих трёх случаях мы и поговорим.

Но сначала надо коротко прояснить один вопрос.

Откуда maven качает библиотеки

На просторах интернета есть сервер, на котором выложены java библиотеки. Этот сервер называется репозиторием, а по-русски — хранилищем. Это не какой-то абстрактный, а вполне конкретный ресурс, адрес которого зашит в дефолтные настройки maven. Поэтому он называется репозиторием по умолчанию. Именно там maven будет искать зависимости из pom.xml.

Как быть, если библиотеки нет в удалённом хранилище по умолчанию, но она есть в другом удалённом хранилище

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

Причины проблемы немного разные, но решение одинаковое — нужно указать репозиторий, в котором зависимости есть. Это либо какой-то другой адрес в Интернете, либо адрес репозитория, поднятого админами в локальной сети.

Как указать maven проекту, где искать дополнительный репозиторий

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

<repositories>
    <repository>
      <id>id репозитория</id>
      <url>адрес репозитория</url>
    </repository>
 </repositories>

Далее мы подключили репозиторий проекта Spring, в котором можно найти последние версии этого семейства бибилиотек. Вот как это выглядит внутри pom.xml

<repository>
    <id>com.springsource.repository.bundles.release </id>
    <url>http://repository.springsource.com/maven/bundles/release </url>
</repository>

Теперь maven, когда не найдёт зависимости в репозитории по умолчанию, или обнаружит, что оный недоступен — не запаникует, а поищет библиотеку в ещё одном репозитории и, если всё идёт по плану, найдёт её там.

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

Как подключить библиотеку, которой в репозиториях нет

Подключить такую библиотеку можно несколькими способами. Например, если у вас есть свой репозиторий в локальной сети, то можно (а иногда даже нужно), положить библиотеку туда, и тем самым свести задачу к предыдущей.

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

Для обработки таких кейсов у maven тоже есть штатный механизм. Только что мы выяснили, как указать maven удалённый репозиторий, отличный от умолчального. Так вот, не обязательно использовать удалённое хранилище. Можно сделать репозиторий в локальной файловой системе, положить туда библиотеку и проинструктировать maven искать зависимости ещё и там.

Как создать свой локальный репозиторий


Для этого, как сказано выше, у maven есть штатное средство.

Допустим у нас есть библиотека, которая находится в jar файле под названием hello-world-library-1.0-SNAPSHOT.jar. О библиотеке нам известно, что в ней есть один класс HelloWorld, который включает один статический метод say, печатающий в консоли, как несложно догадаться, Hello World.

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

mvn \
deploy:deploy-file \
-Durl=file:./lib \
-Dfile=hello-world-library-1.0-SNAPSHOT.jar \
-DgroupId=com.local \
-DartifactId=hello-world-library-local \
-Dpackaging=jar \
-Dversion=1.0-SNAPSHOT

Если вы используете операционную систему Windows, нужно заменить \ на ^, то есть написать

mvn ^
deploy:deploy-file ^
-Durl=file:./lib ^
-Dfile=hello-world-library-1.0-SNAPSHOT.jar ^
-DgroupId=com.local ^
-DartifactId=hello-world-library-local ^
-Dpackaging=jar ^
-Dversion=1.0-SNAPSHOT

Или можно просто убрать \ и написать команду в одну строчку.

mvn deploy:deploy-file -Durl=file:./lib -Dfile=hello-world-library-1.0-SNAPSHOT.jar -DgroupId=com.local -DartifactId=hello-world-library-local -Dpackaging=jar -Dversion=1.0-SNAPSHOT

Обратите внимание, как и для любого другого артефакта, для библиотеки нам нужно придумать groupId, artifactId и version. Мы потом укажем их в pom.xml, когда будем подключать зависимость.

Внутри директории, которую создаст команда, находится полноценный репозиторий, для использования которого достаточно указать maven, где он находится. Все сведения о том, какие библиотеки можно там найти, содержатся непосредственно в структуре директорий свежесозданного репозитория. Для последующего использования репозитория, например, на других машинах, команду deploy-file выполнять не надо.

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

<repositories>
    <repository>
        <id>localrep</id>
        <name>local repository</name>
        <url>file:${project.basedir}/lib</url>
    </repository>
</repositories>

Обратите внимание на пятую строку

<url>file:${project.basedir}/lib</url>

Тут сказано, что искать репозиторий надо в директории проекта, на которую указывает встроенная переменная maven project.basedir.

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

package com;

import static com.HelloWorld.say;

public class Application {
    public static void main(String[] argv) {
        say();
    }
}

Осталось добавить в pom.xml зависимость и можно собирать проект.

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com</groupId>
    <artifactId>library-user-with-local-repository</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>localrep</id>
            <name>local repository</name>
            <url>file:${project.basedir}/lib</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>com.local</groupId>
            <artifactId>hello-world-library-local</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

Проверим:

mvn clean compile exec:java -Dexec.mainClass="com.Application"

Работает!

Директорию lib надо закомитить и библиотека будет доступна проекту вообще всегда.

Однако следует помнить об одном правиле.

Нужно обязательно обновлять номер версии библиотеки в локальном репозитории при каждом изменении jar файла

Maven воспринимает репозитории как внешние, поэтому, если не изменить номер версии, то maven будет использовать не версию библиотеки из директории lib, а ту, что он закешировал на локальной машине. В данном конкретном случае это не должно сыграть роли из-за суффикса SNAPSHOT, но об этом нужно знать.

Есть ещё один распространённый сценарий. У вас есть своя библиотека, которую вы сами собираете с помощью maven и потом подключаете к другому maven проекту.

Как сделать свою java библиотеку


Для того, чтобы сделать библиотеку, достаточно написать класс с модификатором public. И потом можно будет использовать этот класс в коде, к которому подключена библиотека.

Вот такой, например, класс.

package com;

public class HelloWorld {
    public static void say() {
        System.out.println("Hello world");
    }
}

Теперь нужно сделать maven проект, который будет собирать библиотеку, содержащую этот класс.

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

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com</groupId>
    <artifactId>hello-world-library</artifactId>
    <version>1.0-SNAPSHOT</version>
</project>

Итак, у нас есть класс со статическим методом, у нас есть описание артефакта для maven. Осталось только собрать этот код, чтобы получилась библиотека, то есть jar файл.

Просто напишем в консоли:

mvn package

После этого в директории target появится файл с названием \<artifactId>-\<version>.jar, в нашем конткретном случае — hello-world-library-1.0-SNAPSHOT.jar, который и есть ваша библиотека.

Как подключить свежесозданную библиотеку к своему maven проекту


Для того, чтобы библиотеку потом можно было подключать к другому проекту, нужно вместо package написать install.

mvn install

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

Теперь можно сделать новый проект, который будет использовать библиотеку.

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com</groupId>
    <artifactId>hello-world</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com</groupId>
            <artifactId>hello-world-library</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

Внутри проекта будет один класс, который использует статический метод из библиотеки, чтобы сказать Hello world. Мы этот класс уже видели.

package com;

import static com.HelloWorld.say;

public class Application {
    public static void main(String[] argv) {
        say();
    }
}

Проверим ещё раз:

mvn compile exec:java -Dexec.mainClass="com.Application"

Работает не хуже предыдущего варианта!

Что если ваша библиотека использует другую библиотеку?


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

Сделаем библиотеку с непустыми зависимостями.

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com</groupId>
    <artifactId>hello-world-library</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
    </dependencies>
</project>

и напишем для неё код

package com;

import static org.apache.commons.lang3.ArrayUtils.*;

public class HelloWorld {
    public static void say() {
        String[] phrase = {"Hello"};
        phrase = add(phrase, " ");
        phrase = add(phrase, "world");
        for (String word : phrase) {
            System.out.print(word);
        }
        System.out.println();
    }
}

Теперь соберём её

mvn clean install

Перейдём в директорию с нашим проектом, который эту библиотеку использует и попробуем его собрать и запустить.

mvn clean compile exec:java -Dexec.mainClass="com.Application"

И работает!

Как это работает


Строго говоря знать, как процесс устроен внутри, не обязательно, но всё равно очень полезно.

Команда mvn install соберёт библиотеку, а потом положит её в локальный репозиторий по умолчанию. То есть в то же самое место, где лежат все библиотеки, которые вы когда-либо подключали к maven проектам, за исключением, разумеется, тех, которые находятся в локальных репозиториях, сделанных лично вами.

Потом, при сборке проекта, использующего эту библиотеку, maven поищет её в локальном хранилище, найдёт и подключит.

Итого

  • Maven ищет библиотеки в удалённом репозитории по умолчанию.
  • Чтобы подключить библиотеку, которой нет в репозитории по умолчанию, можно указать дополнительные удалённые репозитории, тогда maven будет искать библиотеки ещё и в них.
  • Если библиотеки нет ни в одном удалённом репозитории, то можно с помощью штатного механизма maven создать локальный репозиторий и добавить его в pom.xml.
  • При обновлении репозитория, который находится в исходниках проекта, нужно всегда менять версию библиотеки, иначе могут быть непонятные проблемы.
  • Если у вас есть maven проект, то из него можно сделать библиотеку командой mvn package.
  • Командой mvn install можно поместить библиотеку в локальный репозиторий по умолчанию.
  • Чтобы использовать библиотеку в другом проекте, достаточно указать её в качестве зависимости в pom.xml.

Комментариев нет:

Отправить комментарий