package com.example.starter.config;
import com.example.starter.properties.CustomProperties;
import com.example.starter.service.CustomService;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
@ConditionalOnClass(CustomService.class)
@EnableConfigurationProperties(CustomProperties.class)
@ConditionalOnProperty(
prefix = "custom.service",
name = "enabled",
havingValue = "true",
matchIfMissing = true
)
public class CustomAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CustomService customService(CustomProperties properties) {
return new CustomService(
properties.getApiKey(),
properties.getTimeout(),
properties.getRetryAttempts()
);
}
}
package com.example.starter.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
@ConfigurationProperties(prefix = "custom.service")
public class CustomProperties {
/**
* Enable or disable the custom service
*/
private boolean enabled = true;
/**
* API key for authentication
*/
@NotBlank(message = "API key is required")
private String apiKey;
/**
* Request timeout in milliseconds
*/
@Min(value = 1000, message = "Timeout must be at least 1000ms")
private int timeout = 5000;
/**
* Number of retry attempts
*/
@Min(value = 0, message = "Retry attempts must be non-negative")
private int retryAttempts = 3;
// Getters and Setters
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getRetryAttempts() {
return retryAttempts;
}
public void setRetryAttempts(int retryAttempts) {
this.retryAttempts = retryAttempts;
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.config.CustomAutoConfiguration
<project>
<groupId>com.example</groupId>
<artifactId>custom-spring-boot-starter</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
Spring Boot starters provide autoconfiguration for libraries. I create custom starters to standardize configurations across microservices. Starters contain @Configuration classes with @ConditionalOnClass, @ConditionalOnProperty annotations. spring.factories registers auto-configuration classes. Starters follow naming convention: xxx-spring-boot-starter. Configuration properties use @ConfigurationProperties with metadata for IDE support. Starters encapsulate library setup, reducing boilerplate in applications. Order matters—@AutoConfigureAfter, @AutoConfigureBefore control sequence. Custom starters promote consistency, enable centralized updates, and simplify dependency management. They're essential for maintaining standards across large organizations with many Spring Boot applications.
Related snips
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
package com.example.demo.config;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
Messaging with Apache Kafka
package com.example.demo.controller;
import com.example.demo.dto.FileMetadata;
import com.example.demo.service.FileStorageService;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
File upload and download handling
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
Database migration with Flyway
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
WebSocket for real-time communication
package com.example.demo.model;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
Builder pattern for object construction
Share this code
Here's the card — post it anywhere.