Как создать кастомный Spring Boot Starter?

Ответ

Spring Boot Starter — это артефакт, который предоставляет готовую к использованию конфигурацию для определенной функциональности. Создание своего стартера включает следующие шаги:

1. Структура проекта Создаются два модуля (или два отдельных проекта):

  • autoconfigure: Содержит логику автоконфигурации (@Configuration классы).
  • starter: Пустой jar-файл, который зависит от autoconfigure и необходимых библиотек. Пользователь подключает только этот артефакт.

2. Ядро автоконфигурации (в модуле autoconfigure)

// Файл: ApiAutoConfiguration.java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApiClient.class) // Конфигурация активируется, если класс в classpath
@EnableConfigurationProperties(ApiProperties.class) // Включает кастомные свойства
public class ApiAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean // Создает бин, только если пользователь его не определил
    public ApiClient apiClient(ApiProperties properties) {
        return new ApiClient(properties.getEndpoint(), properties.getTimeout());
    }
}

3. Файл свойств конфигурации

// Файл: ApiProperties.java
@ConfigurationProperties(prefix = "app.api")
public class ApiProperties {
    private String endpoint = "http://default.api";
    private Duration timeout = Duration.ofSeconds(30);
    // геттеры и сеттеры
}

4. Регистрация автоконфигурации В ресурсах модуля autoconfigure создается файл: src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports Содержимое — полное имя класса конфигурации:

com.example.api.autoconfigure.ApiAutoConfiguration

5. Метаданные для подсказок в IDE (опционально, но рекомендуется) В src/main/resources/META-INF/ создается additional-spring-configuration-metadata.json:

{
  "properties": [
    {
      "name": "app.api.endpoint",
      "type": "java.lang.String",
      "description": "Base URL for the external API."
    },
    {
      "name": "app.api.timeout",
      "type": "java.time.Duration",
      "description": "Timeout for API calls.",
      "defaultValue": "30s"
    }
  ]
}

6. Сборка стартера (модуль starter) Его pom.xml должен содержать зависимости на autoconfigure и необходимые библиотеки:

<dependencies>
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>api-spring-boot-autoconfigure</artifactId>
        <version>${project.version}</version>
    </dependency>
    <!-- Другие обязательные библиотеки -->
</dependencies>

Ключевые принципы:

  • Не навязывать: Использовать @Conditional аннотации, чтобы конфигурация активировалась только при наличии нужных классов в classpath.
  • Позволять переопределять: Всегда использовать @ConditionalOnMissingBean, чтобы пользователь мог определить свой бин.
  • Четкое пространство имен: Использовать уникальный префикс для свойств (например, app.api.*).
  • Полное тестирование: Тестировать автоконфигурацию с помощью @SpringBootTest и различных профилей.

Ответ 18+ 🔞

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

Проект, сука, из двух частей Ну, первое, что надо уяснить — это не один кусок, а два, как яйца, блядь. Одно — мозги, другое — обёртка.

  • autoconfigure — это, блядь, мозги. Тут вся логика, вся магия, вся эта ваша ебля с бинами и условиями. Ты сюда конфигурационные классы пихаешь.
  • starter — это, сука, просто пустая банка, обёрточка. Её пользователь и будет подключать. А внутри у неё уже лежат и мозги (autoconfigure), и все остальные нужные библиотеки. Красота, пиздец!

Мозги (autoconfigure) — где всё и решается Вот смотри, создаёшь ты там класс конфигурации. Главное — не навязываться, как навязчивая тёща, блядь. Используй аннотации-условия, чтобы Spring не лез со своей конфигурацией, если её не просили.

@Configuration(proxyBeanMethods = false) // Это для перфоманса, не еби мозг
@ConditionalOnClass(ApiClient.class) // Включится ТОЛЬКО если этот класс в classpath есть. Умно, блядь!
@EnableConfigurationProperties(ApiProperties.class) // Подтягиваем кастомные настройки
public class ApiAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean // Создаст бин, только если пользователь САМ его ещё не создал. Вежливо, сука!
    public ApiClient apiClient(ApiProperties properties) {
        return new ApiClient(properties.getEndpoint(), properties.getTimeout());
    }
}

Настройки, которые можно трогать Ну а как же без свойств, блядь? Пользователь же захочет покрутить ручки. Создаёшь класс для них:

@ConfigurationProperties(prefix = "app.api") // Уникальный префикс, чтобы не было конфликтов, ёпта!
public class ApiProperties {
    private String endpoint = "http://default.api"; // Значение по умолчанию
    private Duration timeout = Duration.ofSeconds(30);
    // Ну и геттеры с сеттерами, куда ж без них, стандартная бюрократия
}

Волшебный файлик, или как Spring узнаёт про твою конфигурацию Вот это, блядь, важный момент! Раньше была ебля с spring.factories, а теперь модно так: В папке ресурсов модуля autoconfigure создаёшь файл: src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports И пишешь в него одну строчку — полное имя своего конфиг-класса:

com.example.api.autoconfigure.ApiAutoConfiguration

Всё, Spring теперь в курсе. Никакой магии, просто договорённость, как в хорошем бандитском притоне.

Чтобы в IDE подсказки были (не обязательно, но охуенно) Ну реально, сделай людям приятно. Создаёшь файл additional-spring-configuration-metadata.json в src/main/resources/META-INF/ и описываешь там свои свойства:

{
  "properties": [
    {
      "name": "app.api.endpoint",
      "type": "java.lang.String",
      "description": "Базовый URL для внешнего API, епт."
    },
    {
      "name": "app.api.timeout",
      "type": "java.time.Duration",
      "description": "Таймаут для вызовов API. Не ставь меньше нуля, мудак.",
      "defaultValue": "30s"
    }
  ]
}

Обёртка (starter) — банка для мозгов Её pom.xml — проще пареной репы. Просто тянешь зависимости на мозги (autoconfigure) и всё остальное, что нужно для работы:

<dependencies>
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>api-spring-boot-autoconfigure</artifactId>
        <version>${project.version}</version>
    </dependency>
    <!-- Сюда все остальные либы, без которых твой клиент — просто груда говна -->
</dependencies>

Главные правила, которые нарушать — себя не уважать:

  1. Не лезь без спроса (@Conditional). Конфигурация должна включаться только когда всё для неё готово. Как гость, который приходит со своим алкоголем.
  2. Дай переопределить (@ConditionalOnMissingBean). Пользователь — царь и бог. Если он захочет свой бин — твой должен уступить, не спорить.
  3. Не сри в чужой огород (уникальный префикс свойств). Используй что-то вроде app.api.*, mycompany.superlib.*. Чтобы не было конфликтов, блядь, это же очевидно!
  4. Проверяй, сука, всё! Пиши тесты на эту автоконфигурацию с @SpringBootTest, гоняй её с разными профилями и условиями. Иначе выкатишь в продакшен — и пиздец, волнение ебать, терпения ноль ебать.

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