Как создать собственный Spring Boot Starter?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Создание собственного Spring Boot Starter
Spring Boot Starter - это удобный способ упаковать и переиспользовать готовый функционал в разных проектах. Рассмотрю пошаговый процесс создания.
Структура Starter-а
Spring Boot Starter состоит из двух частей:
- spring-boot-starter-{name} - POM файл с зависимостями
- spring-boot-{name}-autoconfigure - автоконфигурация и основной код
Шаг 1: Создание автоконфигурационного модуля
Создай Maven модуль spring-boot-elasticsearch-autoconfigure:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-boot-elasticsearch-autoconfigure</artifactId>
<version>1.0.0</version>
<name>Spring Boot Elasticsearch AutoConfigure</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Auto-configuration support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- Conditional compilation support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Your library -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.17.0</version>
</dependency>
</dependencies>
</project>
Шаг 2: Создание Properties класса
package com.example.elasticsearch;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "app.elasticsearch")
public class ElasticsearchProperties {
private String host = "localhost";
private int port = 9200;
private String protocol = "http";
private String username;
private String password;
private long connectionTimeout = 5000;
private long socketTimeout = 30000;
// Getters and setters
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public String getProtocol() { return protocol; }
public void setProtocol(String protocol) { this.protocol = protocol; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public long getConnectionTimeout() { return connectionTimeout; }
public void setConnectionTimeout(long connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public long getSocketTimeout() { return socketTimeout; }
public void setSocketTimeout(long socketTimeout) {
this.socketTimeout = socketTimeout;
}
}
Шаг 3: Создание AutoConfiguration класса
package com.example.elasticsearch;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
@ConditionalOnClass({RestHighLevelClient.class})
@EnableConfigurationProperties(ElasticsearchProperties.class)
public class ElasticsearchAutoConfiguration {
private final ElasticsearchProperties properties;
public ElasticsearchAutoConfiguration(ElasticsearchProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public RestHighLevelClient restHighLevelClient() {
RestClientBuilder builder = RestClient.builder(
new HttpHost(
properties.getHost(),
properties.getPort(),
properties.getProtocol()
)
);
// Добавить authentication если указаны credentials
if (properties.getUsername() != null) {
BasicCredentialsProvider credentialsProvider =
new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(
properties.getUsername(),
properties.getPassword()
)
);
builder.setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
);
}
// Установить timeouts
builder.setRequestConfigCallback(requestConfigBuilder ->
requestConfigBuilder
.setConnectTimeout((int) properties.getConnectionTimeout())
.setSocketTimeout((int) properties.getSocketTimeout())
);
return new RestHighLevelClient(builder);
}
@Bean
@ConditionalOnMissingBean
public ElasticsearchTemplate elasticsearchTemplate(
RestHighLevelClient client) {
return new ElasticsearchTemplate(client);
}
}
Шаг 4: Создание сервиса
package com.example.elasticsearch;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.springframework.stereotype.Service;
@Service
public class ElasticsearchTemplate {
private final RestHighLevelClient client;
public ElasticsearchTemplate(RestHighLevelClient client) {
this.client = client;
}
public void indexDocument(String index, String id, String document)
throws Exception {
IndexRequest request = new IndexRequest(index)
.id(id)
.source(document, org.elasticsearch.common.xcontent.XContentType.JSON);
client.index(request, RequestOptions.DEFAULT);
}
public RestHighLevelClient getClient() {
return client;
}
}
Шаг 5: Регистрация AutoConfiguration
Создай файл src/main/resources/META-INF/spring.factories (или spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports):
Spring Boot 2.7+:
com.example.elasticsearch.ElasticsearchAutoConfiguration
Spring Boot < 2.7:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.elasticsearch.ElasticsearchAutoConfiguration
Шаг 6: Создание Starter POM
Создай отдельный модуль spring-boot-starter-elasticsearch:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-boot-starter-elasticsearch</artifactId>
<version>1.0.0</version>
<name>Spring Boot Elasticsearch Starter</name>
<description>Spring Boot Starter for Elasticsearch</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/>
</parent>
<dependencies>
<!-- Autoconfigure module -->
<dependency>
<groupId>com.example</groupId>
<artifactId>spring-boot-elasticsearch-autoconfigure</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
Шаг 7: Использование Starter в другом проекте
<dependency>
<groupId>com.example</groupId>
<artifactId>spring-boot-starter-elasticsearch</artifactId>
<version>1.0.0</version>
</dependency>
# application.yml
app:
elasticsearch:
host: elasticsearch.local
port: 9200
protocol: https
username: elastic
password: password123
connectionTimeout: 10000
socketTimeout: 60000
@RestController
@RequestMapping("/api")
public class DocumentController {
private final ElasticsearchTemplate template;
public DocumentController(ElasticsearchTemplate template) {
this.template = template;
}
@PostMapping("/documents")
public ResponseEntity<Void> createDocument(
@RequestParam String index,
@RequestParam String id,
@RequestBody String content) throws Exception {
template.indexDocument(index, id, content);
return ResponseEntity.created(URI.create("/documents/" + id)).build();
}
}
Conditional Annotations
Используй для более гибкой конфигурации:
@ConditionalOnClass(RestHighLevelClient.class)
// Bean создается только если класс в classpath
@ConditionalOnMissingBean
// Bean создается только если нет другого bean-а этого типа
@ConditionalOnProperty(prefix = "app.elasticsearch", name = "enabled",
havingValue = "true", matchIfMissing = true)
// Bean создается только если свойство включено
@ConditionalOnWebApplication
// Только для web приложений
Best Practices
- Используй sensible defaults
private String host = "localhost";
private int port = 9200;
- Позволяй пользователям переопределить конфигурацию
@ConditionalOnMissingBean
public RestHighLevelClient restHighLevelClient() { ... }
- Документируй все свойства
@ConfigurationProperties(prefix = "app.elasticsearch")
public class ElasticsearchProperties {
// properties с javadoc
}
-
Включи spring-boot-configuration-processor для IDE support
-
Тестируй автоконфигурацию
@SpringBootTest
public class ElasticsearchAutoConfigurationTest {
@Autowired
private RestHighLevelClient client;
@Test
public void testClientCreated() {
assertNotNull(client);
}
}
-
Используй META-INF/spring.factories для регистрации
-
Версионирование: используй semantic versioning
Spring Boot Starter - это мощный способ создать переиспользуемые компоненты и поделиться ими с другими разработчиками или командами.