Как создать свой Spring Starter?

| June 15, 2022

Что вообще такое starter в Spring framework?

Допустим, Вы разрабатываете несколько приложений или микросервисов на Java. Каждое из них уникальное, и содержит свою собственную бизнес логику. Однако, в каждом из них может быть необходимость использовать общую логику. Например, логику аутентификации, как это часто бывает в мире микросервисов.

Есть несколько способов реализовать общую логику в нескольких приложениях:

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

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

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

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

Так что же такое - starter’ы в Spring framework? Это и есть отдельные библиотеки, со своим циклом релиза, которые позволяют использовать фишки самого Spring’а.

Примеры starter’ов

Возможно, Вы и не задумывались, но каждый раз, когда Вы пользуетесь Spring initilzr’ом, добавляя зависимости в проект, Вы добавляете starter’ы. Например:

  • spring-boot-starter-web - для работы с Spring Web и MVC
  • или spring-boot-starter-test - для возможности написания тестов с подъёмом Spring контейнера

Как создать свой starter?

Это не сложно. Достаточно придерживаться следующего алгоритма:

1. Создадим новый Maven проект

Для этого воспользуемся прелестями автогенерации Maven:

mvn archetype:generate

2. Добавим необходимые зависимости в pom.xml

Следующие строки нужно вставить в dependencies секцию pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.6.7</version>
    <optional>true</optional>
</dependency>

Зависимость объявлена в качестве опциональной, поскольку действительно используемые зависимости будут добавлены самим проектом, в котором и будет использован starter.

3. Добавим Spring конфигурацию

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

Например, представим, что мы должны поддержать работу с следующими property’ями:

demo:
  author: Haggy Waggy
  email: demo@gmail.com

Для этого, создадим класс для работы с ними:

@ConfigurationProperties(prefix = "demo")
public class DemoProperties {
	private String author;

	private String email;
}

Теперь, когда у нас есть описание нужных нам property’ей в Java, мы можем создать класс автоконфигурации нашего starter’а:

@Configuration
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfiguration {
	@Bean
	public DemoService demoService(DemoProperties properties) {
		return new DemoService(properties);
	}
}

И для полноты примера, вот код сервиса, который мы только что использовали в классе автоконфигурации:

public class DemoService {

	private final DemoProperties properties;

	@Autowired
	public DemoService(DemoProperties properties) {
		this.properties = properties;
	}

	public String generateString() {
		return String.format("%s: %s", properties.getAuthor(), properties.getEmail());
	}
}

4. Добавим конфигурацию starter’а

Для этого, создадим файл под названием spring.factories в директории META-INF, в папке с ресурсами приложения (resources):

resources
└── META-INF
    └── spring.factories

В самом файле нужно указать класс автоконфигурации, который мы создали на предыдущем шаге:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.anverbogatov.starter.demo.DemoAutoConfiguration

Сборка

Вот, собственно, и всё.

Что мы имеем в данный момент - мы имеем starter, с собственной конфигурацией, которая будет использована при добавлении starter’а в Spring проекта.

Чтобы проверить, что всё работает, давайте соберём получившийся проект командой:

mvn clean install

После сборки проекта, наш starter будет добавлен в локальный maven репозиторий и таким образом, он станет доступен для использования в локальной разработке других проектов.

Использование starter’а

Добавляем в имеющийся Spring проект зависимость на наш starter. У меня это выглядит следующим образом:

<dependency>
    <groupId>org.example</groupId>
    <artifactId>demo-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

Перекрываем property starter’а в конфигурации приложения:

demo.email=random@mail.ru

Инжектим наш DemoService demoService в какой-либо бин приложения и пробуем вызвать метод сервиса из starter’а. Если всё сделано правильно, сервис вернёт следующую строку:

Haggy Waggy: random@mail.ru

Заключительная часть

В данной статье мы разобрали самый простой сценарий собственного Spring starter’а. Как Вы могли заметить, процесс достаточно не сложный. Самое главное не забывать о том, что каждый starter стоит описывать в собственном пространстве - как пакетном (например, com.dreamteam.starter-demo), так и конфигурационном: starter-demo.demo.author. Это поможет избежать проблем, связанных с пересечением сервисов и конфигруаций между несколькими starter’ами.

Список материалов

Ещё материалы по теме