Skip to content

Auto-configuration ​

CoApi provides comprehensive Spring Boot auto-configuration that simplifies the setup process while maintaining flexibility for custom configurations. The framework supports two distinct activation paths to accommodate different use cases and development preferences.

Overview ​

CoApi's auto-configuration system is designed to automatically detect and configure CoApi clients based on classpath scanning and explicit configuration. The system leverages Spring Boot's conditional configuration mechanism to ensure that CoApi components are only registered when needed, providing a seamless integration experience.

The auto-configuration is built around a modular architecture that combines automatic scanning with manual configuration options, allowing developers to choose the approach that best fits their application architecture.

At-a-Glance ​

FeatureAuto ConfigurationManual Configuration
ActivationSpring Boot auto-configuration@EnableCoApi annotation
ScanningAutomatic classpath scanningExplicit client specification
Base Packages@SpringBootApplication + coapi.base-packagesAnnotation attributes
FlexibilityHigh (combines scanning + registered beans)Medium (explicit control)
Setup ComplexityLow (zero configuration)Low (minimal annotation setup)

Activation Paths ​

Auto (Spring Boot) Configuration Path ​

mermaid
sequenceDiagram
    participant SB as Spring Boot
    participant AC as CoApiAutoConfiguration
    participant CO as ConditionalOnCoApiEnabled
    participant ACR as AutoCoApiRegistrar
    participant ABS as AbstractCoApiRegistrar
    participant CR as CoApiRegistrar
    
    SB->>AC: AutoConfiguration
    AC->>CO: ConditionalOnCoApiEnabled
    CO->>AC: Check coapi.enabled=true (default)
    AC->>AC: Import AutoCoApiRegistrar
    AC->>AC: EnableConfigurationProperties
    AC->>AC: return
    SB->>AC: Instantiate CoApiAutoConfiguration
    AC->>AC: Import AutoCoApiRegistrar
    AC->>ABR: getCoApiDefinitions()
    ABR->>ACR: AbstractCoApiRegistrar.registerBeanDefinitions()
    ACR->>ACR: inferClientMode()
    ACR->>ABR: registerHttpExchangeAdapterFactory()
    ABR->>ACR: register()
    AC->>SB: return AutoCoApiRegistrar
    SB->>CR: CoApiRegistrar.register(apiDefinitions)
    CR->>CR: Register CoApiDefinition beans
    SB->>SB: Complete auto-configuration

Manual Configuration Path ​

mermaid
sequenceDiagram
    participant DC as Developer Configuration
    participant EC as EnableCoApi
    participant ECR as EnableCoApiRegistrar
    participant ABS as AbstractCoApiRegistrar
    participant CR as CoApiRegistrar
    
    DC->>DC: @EnableCoApi(clents=[...])
    DC->>EC: Annotate with explicit clients
    EC->>ECR: @Import(EnableCoApiRegistrar)
    EC->>ECR: Import EnableCoApiRegistrar
    DC->>DC: return
    EC->>ECR: getCoApiDefinitions()
    ECR->>ECR: Extract clients from @EnableCoApi
    ECR->>ECR: Map to CoApiDefinition via toCoApiDefinition()
    ECR->>ABR: AbstractCoApiRegistrar.registerBeanDefinitions()
    ABR->>ECR: inferClientMode()
    ABR->>ECR: registerHttpExchangeAdapterFactory()
    ECR->>ABR: register()
    EC->>CR: CoApiRegistrar.register(apiDefinitions)
    CR->>CR: Register CoApiDefinition beans

Auto-Configuration Components ​

CoApiAutoConfiguration ​

The main auto-configuration class that orchestrates the entire setup process:

kotlin
@AutoConfiguration
@ConditionalOnCoApiEnabled
@Import(AutoCoApiRegistrar::class)
@EnableConfigurationProperties(CoApiProperties::class)
class CoApiAutoConfiguration

This class serves as the entry point for automatic configuration and delegates the actual registration to specialized components.

spring-boot-starter/src/main/kotlin/me/ahoo/coapi/spring/boot/starter/CoApiAutoConfiguration.kt:20

ConditionalOnCoApiEnabled ​

Controls whether CoApi auto-configuration should be enabled:

kotlin
@ConditionalOnProperty(
    value = [ConditionalOnCoApiEnabled.ENABLED_KEY],
    matchIfMissing = true,
    havingValue = "true",
)
annotation class ConditionalOnCoApiEnabled {
    companion object {
        const val ENABLED_KEY: String = COAPI_PREFIX + ENABLED_SUFFIX_KEY
    }
}

The configuration is enabled by default and can be disabled by setting coapi.enabled=false in application properties.

spring-boot-starter/src/main/kotlin/me/ahoo/coapi/spring/boot/starter/ConditionalOnCoApiEnabled.kt:18

AutoCoApiRegistrar ​

Handles automatic scanning and registration of CoApi clients:

mermaid
graph TD
    A["AutoCoApiRegistrar"] --> B["getCoApiDefinitions()"]
    A --> C["getScanBasePackages()"]
    B --> D["Scanned Definitions"]
    B --> E["Registered CoApiDefinition Beans"]
    C --> F["AutoConfigurationPackages.get()"]
    C --> G["coapi.base-packages Property"]
    D --> H["ApiClientScanner"]
    H --> I["ClassPathScanningCandidateComponentProvider"]
    I --> J["AnnotationTypeFilter(CoApi.class)"]
    I --> K["Interface Only Filter"]

The registrar combines automatically scanned interface definitions with any explicitly registered CoApiDefinition beans. Since v2.2.0, name conflicts within the merged set (e.g. a registered definition colliding with a scanned interface of the same client name) fail startup with an IllegalStateException listing the conflicting types.

Declare CoApiDefinition beans with a static @Bean method

CoApiDefinition beans are read while bean definitions are still being registered, before any BeanPostProcessor exists. A non-static @Bean method therefore forces its configuration class to be created too early: its @Autowired/@Value fields are not injected and its @Bean methods are not proxied. CoApi logs a warning for such methods since v2.3.0 and fails startup since v3.0.0. Declare the method static (Kotlin: @JvmStatic in a companion object) — the same rule Spring applies to BeanFactoryPostProcessor beans. Do not use @Value parameters (they are not resolved yet at that point): since v3.0.0 CoApi resolves ${...} placeholders and the lb:// scheme in a definition bean's baseUrl itself, exactly as for the @CoApi annotation:

kotlin
@Configuration
class OrderApiConfiguration {
    companion object {
        @JvmStatic
        @Bean
        fun orderApiDefinition(): CoApiDefinition = CoApiDefinition(
            name = "OrderApi",
            apiType = OrderApi::class.java,
            baseUrl = "\${order.url}", // resolved by CoApi (v3.0.0+); on v2.3.x resolve it via an Environment parameter
            loadBalanced = false,
        )
    }
}
kotlin
private fun getScanBasePackages(): Set<String> {
    val coApiBasePackages = getCoApiBasePackages()
    if (AutoConfigurationPackages.has(appContext).not()) {
        return coApiBasePackages
    }
    return AutoConfigurationPackages.get(appContext).toSet() + coApiBasePackages
}

spring-boot-starter/src/main/kotlin/me/ahoo/coapi/spring/boot/starter/AutoCoApiRegistrar.kt:48

EnableCoApiRegistrar ​

Handles manual configuration through the @EnableCoApi annotation:

kotlin
@Suppress("UNCHECKED_CAST")
override fun getCoApiDefinitions(importingClassMetadata: AnnotationMetadata): Set<CoApiDefinition> {
    val enableCoApi =
        importingClassMetadata.getAnnotationAttributes(EnableCoApi::class.java.name) ?: return emptySet()
    val clients = enableCoApi[EnableCoApi::clients.name] as Array<Class<*>>
    return clients.map { clientType ->
        clientType.toCoApiDefinition(env)
    }.toSet()
}

spring/src/main/kotlin/me/ahoo/coapi/spring/EnableCoApiRegistrar.kt:22

AbstractCoApiRegistrar ​

Provides the template method pattern for bean registration:

mermaid
sequenceDiagram
    participant ACR as AbstractCoApiRegistrar
    participant CM as inferClientMode
    participant RH as registerHttpExchangeAdapterFactory
    participant CR as CoApiRegistrar
    
    ACR->>ACR: registerBeanDefinitions()
    ACR->>CM: inferClientMode()
    CM->>ACR: return ClientMode
    ACR->>RH: registerHttpExchangeAdapterFactory(clientMode, registry)
    RH->>RH: Register SyncHttpExchangeAdapterFactory or ReactiveHttpExchangeAdapterFactory
    ACR->>ACR: getCoApiDefinitions(importingClassMetadata)
    ACR->>CR: CoApiRegistrar(registry, clientMode)
    ACR->>CR: register(apiDefinitions)
    CR->>CR: Register CoApiDefinition beans

The abstract class implements the Spring ImportBeanDefinitionRegistrar interface and provides a structured approach to bean registration.

kotlin
override fun registerBeanDefinitions(importingClassMetadata: AnnotationMetadata, registry: BeanDefinitionRegistry) {
    val clientMode = inferClientMode {
        env.getProperty(it)
    }
    registerHttpExchangeAdapterFactory(clientMode, registry)
    val coApiRegistrar = CoApiRegistrar(registry, clientMode)
    val apiDefinitions = getCoApiDefinitions(importingClassMetadata)
    coApiRegistrar.register(apiDefinitions)
}

spring/src/main/kotlin/me/ahoo/coapi/spring/AbstractCoApiRegistrar.kt:42

Classpath Scanning Process ​

The automatic scanning process uses a specialized component scanner that finds interfaces annotated with @CoApi:

kotlin
class ApiClientScanner(useDefaultFilters: Boolean, environment: Environment) :
    ClassPathScanningCandidateComponentProvider(useDefaultFilters, environment) {
    init {
        addIncludeFilter(AnnotationTypeFilter(CoApi::class.java))
    }

    override fun isCandidateComponent(beanDefinition: AnnotatedBeanDefinition): Boolean {
        return beanDefinition.metadata.isInterface
    }
}

spring-boot-starter/src/main/kotlin/me/ahoo/coapi/spring/boot/starter/AutoCoApiRegistrar.kt:72

The scanning combines multiple base package sources:

  1. Spring Boot Auto-configuration packages: Automatically detected from the main application class
  2. CoApi-specific packages: Configured via coapi.base-packages property
  3. Supports both single string and array notation:
    • Single package: coapi.base-packages=com.example.clients
    • Multiple packages: coapi.base-packages[0]=com.example.clients,coapi.base-packages[1]=com.example.external

Bean Registration Sequence ​

mermaid
graph TD
    A["Spring Boot Auto-configuration"] --> B["CoApiAutoConfiguration"]
    B --> C["AutoCoApiRegistrar"]
    C --> D["Conditional Check"]
    D --> E{"coapi.enabled=true?"}
    E -->|Yes| F["getCoApiDefinitions()"]
    E -->|No| G["Skip Configuration"]
    F --> H["getScanBasePackages()"]
    H --> I["AutoConfigurationPackages.get()"]
    H --> J["coapi.base-packages"]
    I --> K["Scan CoApi Interfaces"]
    J --> K
    K --> L["ApiClientScanner.findCandidateComponents()"]
    L --> M["Create CoApiDefinition"]
    M --> N["CoApiRegistrar.register()"]
    N --> O["Register HttpExchangeAdapterFactory"]
    O --> P["Register Client Beans"]

Configuration Properties ​

The auto-configuration supports several properties through CoApiProperties:

PropertyDefaultDescription
coapi.enabledtrueEnable/disable CoApi auto-configuration
coapi.base-packages(empty)Comma-separated list of base packages to scan
coapi.client-modereactiveClient mode: sync or reactive

Spring Boot Auto-configuration Registration ​

The auto-configuration is registered in the Spring Boot auto-configuration metadata:

properties
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
me.ahoo.coapi.spring.boot.starter.CoApiAutoConfiguration

spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:1

This ensures that Spring Boot automatically discovers and includes the CoApi auto-configuration when the starter dependency is present in the classpath.

References ​

  1. Spring Boot Auto-Configuration Documentation
  2. Spring Boot @Conditional Annotations
  3. Spring ClassPathScanningCandidateComponentProvider
  4. Spring ImportBeanDefinitionRegistrar Interface
  5. CoApi @EnableCoApi Annotation
  6. [CoApi Client Configuration](.././customization