The full picture
// module 1: acme-audit-spring-boot-autoconfigure
@AutoConfiguration
@ConditionalOnClass(AuditClient.class)
@EnableConfigurationProperties(AcmeAuditProperties.class)
public class AcmeAuditAutoConfiguration {
@Bean
@ConditionalOnMissingBean // team can replace it
@ConditionalOnProperty(prefix = "acme.audit", // team can disable it
name = "enabled", havingValue = "true", matchIfMissing = true)
AuditClient auditClient(AcmeAuditProperties props) {
return new AuditClient(props.endpoint(), props.bufferSize());
}
}
// + META-INF/spring/…AutoConfiguration.imports listing the class
// + config metadata json -> IDE completion for acme.audit.*
// module 2: acme-audit-spring-boot-starter — empty jar, POM depends on
// autoconfigure module + audit client lib. Teams add ONE dependency.- Why platform teams love this: upgrades ship as version bumps (Renovate PRs) instead of migration wikis; compliance ('all services must log X') becomes a dependency, auditable from the build graph.
- Design discipline: never force — every bean
@ConditionalOnMissingBean, every feature toggleable by property, sensiblematchIfMissing. A starter that can't be overridden becomes a fork magnet. - Version alignment: manage through your own BOM importing Spring Boot's; starters should not pin versions Spring's BOM already manages.
- Testing:
ApplicationContextRunnerlets you assert conditions in plain unit tests — starter without tests = platform-wide blast radius.