IdempotentAutoConfiguration.kt
package io.github.lishangbu.avalon.idempotent.autoconfigure
import io.github.lishangbu.avalon.idempotent.aspect.IdempotentAspect
import io.github.lishangbu.avalon.idempotent.key.IdempotentKeyResolver
import io.github.lishangbu.avalon.idempotent.key.SpelIdempotentKeyResolver
import io.github.lishangbu.avalon.idempotent.lease.DefaultIdempotentLeaseManager
import io.github.lishangbu.avalon.idempotent.lease.IdempotentLeaseManager
import io.github.lishangbu.avalon.idempotent.properties.IdempotentProperties
import io.github.lishangbu.avalon.idempotent.store.IdempotentStore
import io.github.lishangbu.avalon.idempotent.store.JdbcIdempotentStore
import io.github.lishangbu.avalon.idempotent.store.RedisIdempotentStore
import org.aspectj.lang.ProceedingJoinPoint
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
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
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.jdbc.core.JdbcTemplate
import tools.jackson.databind.json.JsonMapper
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
/**
* Auto-configures Redis-backed idempotent execution support.
*/
@AutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ProceedingJoinPoint::class)
@ConditionalOnProperty(
prefix = IdempotentProperties.PREFIX,
name = ["enabled"],
havingValue = "true",
matchIfMissing = true,
)
@EnableConfigurationProperties(IdempotentProperties::class)
class IdempotentAutoConfiguration {
@Bean
@ConditionalOnMissingBean(JsonMapper::class)
fun idempotentJsonMapper(): JsonMapper = JsonMapper.builder().findAndAddModules().build()
@Bean
@ConditionalOnMissingBean(IdempotentKeyResolver::class)
fun idempotentKeyResolver(properties: IdempotentProperties): IdempotentKeyResolver = SpelIdempotentKeyResolver(properties)
@Bean
@ConditionalOnBean(StringRedisTemplate::class)
@ConditionalOnMissingBean(IdempotentStore::class)
@ConditionalOnProperty(
prefix = IdempotentProperties.PREFIX,
name = ["store-type"],
havingValue = "REDIS",
matchIfMissing = true,
)
fun idempotentStore(
stringRedisTemplate: StringRedisTemplate,
jsonMapper: JsonMapper,
): IdempotentStore = RedisIdempotentStore(stringRedisTemplate, jsonMapper)
@Bean
@ConditionalOnBean(JdbcTemplate::class)
@ConditionalOnMissingBean(IdempotentStore::class)
@ConditionalOnProperty(
prefix = IdempotentProperties.PREFIX,
name = ["store-type"],
havingValue = "JDBC",
)
fun jdbcIdempotentStore(
properties: IdempotentProperties,
jdbcTemplate: JdbcTemplate,
): IdempotentStore = JdbcIdempotentStore(jdbcTemplate, properties.jdbcTableName)
@Bean(destroyMethod = "shutdown")
@ConditionalOnBean(IdempotentStore::class)
@ConditionalOnMissingBean(name = ["idempotentLeaseExecutor"])
fun idempotentLeaseExecutor(): ScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor { runnable ->
Thread(runnable, "idempotent-renewal").apply {
isDaemon = true
}
}
@Bean
@ConditionalOnBean(IdempotentStore::class)
@ConditionalOnMissingBean(IdempotentLeaseManager::class)
fun idempotentLeaseManager(
properties: IdempotentProperties,
idempotentStore: IdempotentStore,
idempotentLeaseExecutor: ScheduledExecutorService,
): IdempotentLeaseManager = DefaultIdempotentLeaseManager(properties, idempotentStore, idempotentLeaseExecutor)
@Bean
@ConditionalOnBean(IdempotentStore::class)
@ConditionalOnMissingBean
fun idempotentAspect(
properties: IdempotentProperties,
keyResolver: IdempotentKeyResolver,
idempotentLeaseManager: IdempotentLeaseManager,
idempotentStore: IdempotentStore,
jsonMapper: JsonMapper,
): IdempotentAspect = IdempotentAspect(properties, keyResolver, idempotentLeaseManager, idempotentStore, jsonMapper)
}