diff --git a/forgeboot-cache/.gitattributes b/forgeboot-cache/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-cache/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-cache/.gitignore b/forgeboot-cache/.gitignore new file mode 100644 index 0000000..cd70ec8 --- /dev/null +++ b/forgeboot-cache/.gitignore @@ -0,0 +1,39 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-cache/build.gradle.kts b/forgeboot-cache/build.gradle.kts new file mode 100644 index 0000000..a51e0e0 --- /dev/null +++ b/forgeboot-cache/build.gradle.kts @@ -0,0 +1,8 @@ +extra { + // 标记为根项目 + setProperty(ProjectFlags.IS_ROOT_MODULE, true) +} + +dependencies { + +} diff --git a/forgeboot-cache/forgeboot-cache-api/.gitattributes b/forgeboot-cache/forgeboot-cache-api/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-cache/forgeboot-cache-api/.gitignore b/forgeboot-cache/forgeboot-cache-api/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-cache/forgeboot-cache-api/build.gradle.kts b/forgeboot-cache/forgeboot-cache-api/build.gradle.kts new file mode 100644 index 0000000..0ad9af0 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/build.gradle.kts @@ -0,0 +1,5 @@ + +dependencies { + compileOnly(libs.springBoot.autoconfigure) + kapt(libs.springBoot.configuration.processor) +} diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CacheEvictEx.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CacheEvictEx.kt new file mode 100644 index 0000000..da76d9e --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CacheEvictEx.kt @@ -0,0 +1,20 @@ +package com.gewuyou.forgeboot.cache.api.annotations + +/** + * 缓存移除扩展注解,用于标识需要清除缓存的方法 + * + * 该注解应用于方法上,表示在方法执行前后可以触发缓存清除操作。 + * 支持通过命名空间和SpEL表达式定义缓存键,并指定缓存层级。 + * + * @property namespace 缓存的命名空间,用于对缓存进行逻辑分组,默认为空字符串 + * @property keySpEL 缓存键的SpEL表达式,用于动态生成缓存键,默认为空字符串 + * + * @since 2025-06-16 22:29:52 + * @author gewuyou + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class CacheEvictEx( + val namespace: String, + val keySpEL: String +) \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CachePutEx.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CachePutEx.kt new file mode 100644 index 0000000..3d180b1 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CachePutEx.kt @@ -0,0 +1,21 @@ +package com.gewuyou.forgeboot.cache.api.annotations + +/** + * 缓存 put ex + * + * 用于声明式缓存更新操作的注解,适用于方法级别 + * + * @property namespace 命名空间,用于缓存键的逻辑分组,默认为空字符串 + * @property keySpEL 缓存键的 SpEL 表达式,默认为空字符串 + * @property ttl 缓存键生存时间 + * + * @since 2025-06-16 22:33:35 + * @author gewuyou + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class CachePutEx( + val namespace: String, + val keySpEL: String, + val ttl: Long = 300L +) \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CacheableEx.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CacheableEx.kt new file mode 100644 index 0000000..77540f4 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/annotations/CacheableEx.kt @@ -0,0 +1,29 @@ +package com.gewuyou.forgeboot.cache.api.annotations + +import kotlin.reflect.KClass + +/** + * 缓存注解扩展,用于标记可缓存的方法。 + * + * 该注解应用于方法级别,表示该方法的结果可以被缓存。通过指定不同的参数, + * 可以控制缓存的命名空间、键生成表达式、过期时间、是否缓存空值以及缓存层级。 + * + * @property namespace 缓存的命名空间,用于区分不同业务场景下的缓存数据,默认为空字符串。 + * @property keySpEL 缓存键的 SpEL 表达式,用于动态生成缓存键,默认为空字符串。 + * @property ttl 缓存过期时间,单位为秒,默认值为 300 秒(即 10 分钟)。 + * @property cacheNull 是否缓存空值,默认为 false,表示不缓存空结果。 + * @property type 指定缓存值的类型,用于确保类型安全,默认使用 KClass<*> 表示任意类型。 + * + * @since 2025-06-16 + * @author gewuyou + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +annotation class CacheableEx( + val namespace: String, + val keySpEL: String, + val ttl: Long = 300L, + val cacheNull: Boolean = false, + @JvmSuppressWildcards val type: KClass<*> = Any::class +) \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/config/CacheProperties.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/config/CacheProperties.kt new file mode 100644 index 0000000..031251c --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/config/CacheProperties.kt @@ -0,0 +1,31 @@ +package com.gewuyou.forgeboot.cache.api.config + +import org.springframework.boot.context.properties.ConfigurationProperties +import java.time.Duration + +/** + * 缓存属性配置类 + * + * 该类用于定义缓存相关的配置属性,通过@ConfigurationProperties绑定配置前缀"forgeboot.cache" + * 支持配置默认缓存过期时间(TTL) + * + * @since 2025-06-17 10:04:48 + * @author gewuyou + */ +@ConfigurationProperties("forgeboot.cache") +class CacheProperties { + /** + * 默认缓存过期时间(Time To Live) + * + * 用于指定缓存项在未被访问后的最大存活时间 + * 默认值为15分钟 + */ + var theDefaultCacheTTL: Duration = Duration.ofMinutes(15) + /** + * 缓存项为null时使用的占位符 + * + * 用于在缓存项为null时返回的占位符 + * 默认值为"__NULL__" + */ + var nullValuePlaceholder: String = "__NULL__" +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/contract/Cache.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/contract/Cache.kt new file mode 100644 index 0000000..7890cd6 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/contract/Cache.kt @@ -0,0 +1,69 @@ +package com.gewuyou.forgeboot.cache.api.contract + +import java.time.Duration + +/** + * 缓存接口定义 + * + * 提供统一的缓存操作方法,包括获取、存储、删除和判断键是否存在。 + * + * @since 2025-06-16 22:39:16 + * @author gewuyou + */ +interface Cache { + + /** + * 获取指定键对应的缓存值。 + * + * @param key 缓存键 + * @return 缓存值,如果不存在则返回 null + */ + fun retrieve(key: String): String? + + /** + * 将键值对存储到缓存中,并设置过期时间。 + * + * @param key 缓存键 + * @param value 缓存值,可以为 null + * @param ttl 缓存存活时间(Time To Live) + */ + fun put(key: String, value: String?, ttl: Duration) + + /** + * 删除指定键的缓存。 + * + * @param key 要删除的缓存键 + * @return 如果删除成功返回 true,否则返回 false + */ + fun remove(key: String): Boolean + + /** + * 判断指定键是否存在于缓存中。 + * + * @param key 缓存键 + * @return 如果存在返回 true,否则返回 false + */ + fun exists(key: String): Boolean + /** + * 清空缓存中的所有数据。 + * @param namespace 命名空间 + */ + fun clear(namespace: String) + + /** + * 通过操作符重载实现缓存的获取操作。 + * + * @param key 缓存键 + * @return 缓存值,如果不存在则返回 null + */ + fun Cache.get(key: String): String? = this.retrieve(key) + + /** + * 通过操作符重载实现缓存的存储操作。 + * + * @param key 缓存键 + * @param value 缓存值,可以为 null + * @param ttl 缓存存活时间(Time To Live) + */ + operator fun Cache.set(key: String, value: String?, ttl: Duration) = this.put(key, value, ttl) +} diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/entities/CacheLayer.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/entities/CacheLayer.kt new file mode 100644 index 0000000..28e857e --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/entities/CacheLayer.kt @@ -0,0 +1,17 @@ +package com.gewuyou.forgeboot.cache.api.entities + +import com.gewuyou.forgeboot.cache.api.contract.Cache + +/** + * 缓存层实体类,用于定义缓存层级结构。 + * + * @property cache 关联的缓存实例,用于实际执行缓存操作。 + * @property priority 缓存优先级,用于决定多个缓存实现中的调用顺序。 + * + * @since 2025-06-16 22:43:15 + * @author gewuyou + */ +data class CacheLayer( + val cache: Cache, + val priority: Int +) diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/exception/CacheException.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/exception/CacheException.kt new file mode 100644 index 0000000..dc05e18 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/exception/CacheException.kt @@ -0,0 +1,17 @@ +package com.gewuyou.forgeboot.cache.api.exception + +/** + * 缓存异常类,用于封装与缓存操作相关的异常信息。 + * + * @param message 异常的详细描述信息,默认为 null。 + * @param cause 导致此异常的底层异常,默认为 null。 + * + * @since 2025-06-17 21:14:40 + * @author gewuyou + */ +class CacheException( + message: String? = null, + cause: Throwable? = null, +) : RuntimeException( + message, cause +) \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/extension/CacheExtensions.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/extension/CacheExtensions.kt new file mode 100644 index 0000000..8219d3d --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/extension/CacheExtensions.kt @@ -0,0 +1,32 @@ +package com.gewuyou.forgeboot.cache.api.extension + +import com.gewuyou.forgeboot.cache.api.contract.Cache +import java.time.Duration + +/** + * 重载 set 操作符,用于通过 [] 语法将指定键值对及存活时间存入缓存。 + * + * 此方法封装了缓存的存储操作,并支持指定缓存项的存活时间(TTL)。 + * + * @param key 要存储的缓存键,类型为 [String]。 + * @param value 要存储的缓存值,类型为 [String?],允许为 null。 + * @param ttl 存活时间,类型为 [Duration],表示缓存项的有效期。 + */ +operator fun Cache.set(key: String, value: String?, ttl: Duration) { + this.put(key, value, ttl) +} + +/** + * 获取指定键对应的缓存值。 + * + * 从最高优先级的缓存层开始查找,一旦找到有效值,则将其回填到 + * 所有优先级高于当前层的缓存中,并返回该值。 + * + * 此方法封装了缓存的读取逻辑,并负责维护缓存层级间的数据一致性。 + * + * @param key 要获取的缓存键,类型为 [String]。 + * @return 如果存在缓存值则返回对应值,类型为 [String?];否则返回 null。 + */ +operator fun Cache.get(key: String): String? { + return this.retrieve(key) +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/extension/CacheServiceExtensions.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/extension/CacheServiceExtensions.kt new file mode 100644 index 0000000..5c9efe8 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/extension/CacheServiceExtensions.kt @@ -0,0 +1,77 @@ +package com.gewuyou.forgeboot.cache.api.extension + +import com.gewuyou.forgeboot.cache.api.loader.CacheLoader +import com.gewuyou.forgeboot.cache.api.service.CacheService +import java.time.Duration + +/** + * 操作符扩展函数,用于通过 set(key, value, ttl) 存储带有过期时间的泛型值。 + * + * 该函数扩展了 CacheService 接口,允许使用操作符语法设置缓存键值对,并指定生存时间。 + * 实际调用 put 方法完成缓存写入操作。 + * + * @param key 缓存键,用于唯一标识存储的数据 + * @param value 值,支持任意对象类型,将被序列化后存储 + * @param ttl 缓存生存时间,指定该值后缓存将在设定的时间后自动失效 + */ +operator fun CacheService.set(key: String, value: Any, ttl: Duration) { + this.put(key, value, ttl) +} + +/** + * 获取指定键的缓存值。 + * + * @param key 缓存键 + * @param type 值的类型 Class + * @return 缓存中的对象,如果不存在则为 null + */ +operator fun CacheService.get(key: String, type: Class): T? { + return this.retrieve(key, type) +} + +/** + * 通过泛型方式获取指定键的缓存值。 + * + * @param key 缓存键 + * @return 缓存中的对象,如果不存在则为 null + */ +inline fun CacheService.get(key: String): T? { + return this.retrieve(key, T::class.java) +} +/** + * 获取指定键的缓存值,若不存在则使用给定的加载器进行加载,并将结果存入缓存。 + * + * @param key 缓存键 + * @param type 值的类型 Class + * @param ttl 缓存生存时间,指定该值后缓存将在设定的时间后自动失效 + * @param loader 用于加载数据的 CacheLoader 实例 + * @return 缓存中的对象,如果不存在且 loader 也无法加载则为 null + */ +fun CacheService.getOrLoad( + key: String, + type: Class, + ttl: Duration, + loader: CacheLoader = CacheLoader { _, _ -> null } +): T? { + return this.retrieve(key, type) ?: loader.load(key, type)?.also { + this.put(key, it, ttl) + } +} + +/** + * 通过泛型方式获取指定键的缓存值,若不存在则使用给定的加载函数进行加载,并将结果存入缓存。 + * + * @param key 缓存键 + * @param ttl 缓存生存时间,指定该值后缓存将在设定的时间后自动失效 + * @param loader 一个函数,接收键并返回加载的数据(或 null) + * @return 缓存中的对象,如果不存在且 loader 也无法加载则为 null + */ +inline fun CacheService.getOrLoad( + key: String, + ttl: Duration, + loader: CacheLoader = CacheLoader { _, _ -> null } +): T? { + return retrieve(key, T::class.java) ?: loader.load(key, T::class.java)?.also { + put(key, it, ttl) + } +} diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/generator/KeyGenerator.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/generator/KeyGenerator.kt new file mode 100644 index 0000000..bb79d1a --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/generator/KeyGenerator.kt @@ -0,0 +1,32 @@ +package com.gewuyou.forgeboot.cache.api.generator + +/** + * 键生成器接口 + * + * 该接口用于统一管理不同场景下的缓存键生成逻辑,通过命名空间和基础键值组合生成完整键。 + * + * @since 2025-06-16 22:17:36 + * @author gewuyou + */ +interface KeyGenerator { + /** + * 生成缓存键 + * + * 根据传入的命名空间和基础键值,组合生成一个完整的缓存键。实现类应确保返回的键具有唯一性与可读性。 + * + * @param namespace 命名空间,用于隔离不同的缓存区域,例如模块名称或业务标识 + * @param key 基础键值,表示具体缓存项标识,通常是动态参数或固定字符串 + * @return 返回生成的完整缓存键,通常格式为 "namespace:key" + */ + fun generateKey(namespace: String, key: String): String + + /** + * 获取连接符 + * + * 返回用于拼接命名空间和基础键值的连接符,默认实现可能返回冒号(:)或其他特定字符。 + * 此方法允许子类自定义连接符,以满足不同存储结构或命名规范的需求。 + * + * @return 返回连接符字符串 + */ + fun getConnectors(): String +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/loader/CacheLoader.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/loader/CacheLoader.kt new file mode 100644 index 0000000..7c7bd77 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/loader/CacheLoader.kt @@ -0,0 +1,18 @@ +package com.gewuyou.forgeboot.cache.api.loader + + +/** + * 通用缓存加载器接口,支持缓存未命中时从源头加载数据。 + * + * @since 2025-06-16 + */ +fun interface CacheLoader { + /** + * 加载与指定缓存键关联的数据。 + * + * @param key 缓存键(字符串形式) + * @param type 缓存值类型(用于反序列化) + * @return 加载到的数据,若无则返回 null + */ + fun load(key: String, type: Class): T? +} diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/manager/CacheManager.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/manager/CacheManager.kt new file mode 100644 index 0000000..3a86068 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/manager/CacheManager.kt @@ -0,0 +1,37 @@ +package com.gewuyou.forgeboot.cache.api.manager + +import com.gewuyou.forgeboot.cache.api.service.CacheService + +/** + * 缓存管理器 + * + * 提供对不同缓存服务的统一访问接口,通过指定的命名空间获取对应的缓存服务实例。 + * 该接口定义了核心方法用于获取泛型化的缓存服务对象。 + * + * @since 2025-06-16 22:11:06 + * @author gewuyou + */ +interface CacheManager { + + /** + * 获取指定命名空间的缓存服务实例 + * + * @param namespace 缓存服务的命名空间标识 + * @return 返回与命名空间关联的 CacheService 实例 + */ + fun getCache(namespace: String): CacheService + + /** + * 清除指定命名空间下的所有缓存数据 + * + * @param namespace 要清除缓存数据的命名空间标识 + */ + fun clear(namespace: String) + + /** + * 清除所有命名空间下的缓存数据 + * + * 通常用于全局缓存刷新或系统清理操作 + */ + fun clearAll() +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/policy/CachePolicy.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/policy/CachePolicy.kt new file mode 100644 index 0000000..b756e15 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/policy/CachePolicy.kt @@ -0,0 +1,28 @@ +package com.gewuyou.forgeboot.cache.api.policy + +/** + * 缓存策略接口,定义了缓存数据应用的规则。 + * + * 实现该接口的类应提供一个 apply 方法,用于根据给定的键和值来执行特定的缓存策略。 + * 例如,可以在此方法中实现过期时间设置、缓存淘汰算法等逻辑。 + * + * @since 2025-06-21 11:54:50 + * @author gewuyou + */ +interface CachePolicy { + /** + * 根据给定的键和值来执行缓存策略。 + * + * @param key 缓存项的键,用于唯一标识一个缓存条目 + * @param value 可为空的字符串,表示与键关联的值;为 null 时可能指示移除或跳过缓存操作 + * @return 返回处理后的缓存值,也可以是 null 表示不进行缓存 + */ + fun apply(key: String, value: String?): String? + + /** + * 获取该策略的优先级,用于决定多个策略的执行顺序 + * + * @return 优先级,值越小表示优先级越高 + */ + fun getOrder(): Int +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/policy/NullValuePolicy.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/policy/NullValuePolicy.kt new file mode 100644 index 0000000..4bf8e6b --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/policy/NullValuePolicy.kt @@ -0,0 +1,34 @@ +package com.gewuyou.forgeboot.cache.api.policy + +/** + * Null 值策略接口 + * + * 用于定义缓存中 null 值的处理策略,包括是否允许缓存 null 值、获取 null 占位符以及判断值是否为 null 占位符。 + * + * @since 2025-06-16 22:15:13 + * @author gewuyou + */ +interface NullValuePolicy { + /** + * 判断给定的 key 是否允许缓存 null 值。 + * + * @param key 缓存项的键 + * @return Boolean 返回 true 表示允许缓存 null 值,否则不允许 + */ + fun allowCacheNull(key: String): Boolean + + /** + * 获取用于表示 null 值的占位符字符串。 + * + * @return String 返回 null 值的占位符 + */ + fun nullPlaceholder(): String + + /** + * 判断给定的 value 是否为 null 占位符。 + * + * @param value 要判断的值 + * @return Boolean 返回 true 表示是 null 占位符,否则不是 + */ + fun isNullPlaceholder(value: String): Boolean +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/CacheService.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/CacheService.kt new file mode 100644 index 0000000..596b7aa --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/CacheService.kt @@ -0,0 +1,54 @@ +package com.gewuyou.forgeboot.cache.api.service + +import java.time.Duration + + +/** + * 缓存服务接口,定义了缓存的基本操作。 + * + * @since 2025-06-16 21:58:56 + * @author gewuyou + */ +interface CacheService { + + /** + * 获取指定键的缓存值。 + * + * @param key 缓存键 + * @param type 值的类型 Class + * @return 缓存中的对象,如果不存在则为 null + */ + fun retrieve(key: String, type: Class): T? + + /** + * 设置缓存值。 + * + * @param key 缓存键 + * @param value 值(支持任意对象) + * @param ttl 缓存时长,null 表示使用默认 TTL + */ + fun put(key: String, value: Any, ttl: Duration? = null) + + /** + * 删除缓存。 + * + * @param key 缓存键 + * @return 删除成功返回 true + */ + fun remove(key: String): Boolean + + /** + * 判断缓存是否存在。 + * + * @param key 缓存键 + * @return 如果存在返回 true,否则返回 false + */ + fun exists(key: String): Boolean + + /** + * 清空指定命名空间下的缓存。 + * + * @param namespace 命名空间 + */ + fun clear(namespace: String) +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/CacheWarmUpService.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/CacheWarmUpService.kt new file mode 100644 index 0000000..072d543 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/CacheWarmUpService.kt @@ -0,0 +1,16 @@ +package com.gewuyou.forgeboot.cache.api.service + +/** + * 缓存预热服务 + * 用于在系统启动或特定时机提前加载常用缓存数据,提升后续请求的访问效率 + * + * @since 2025-06-16 22:18:29 + * @author gewuyou + */ +fun interface CacheWarmUpService { + /** + * 执行缓存预热操作 + * 该方法应包含具体的缓存加载逻辑,例如查询数据库或调用其他服务获取数据并写入缓存 + */ + fun warmUp() +} diff --git a/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/LockService.kt b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/LockService.kt new file mode 100644 index 0000000..06ac67d --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-api/src/main/kotlin/com/gewuyou/forgeboot/cache/api/service/LockService.kt @@ -0,0 +1,43 @@ +package com.gewuyou.forgeboot.cache.api.service + +import java.time.Duration + +/** + * 锁服务接口,用于在分布式环境中执行需要加锁的操作。 + * + * @since 2025-06-16 22:11:57 + * @author gewuyou + */ +interface LockService { + + /** + * 在锁的保护下执行指定操作。 + * + * 使用给定的 key 获取锁,并在指定的超时时间内执行 supplier 提供的操作。 + * 如果无法在超时时间内获取锁,则可能抛出异常或返回默认值,具体取决于实现方式。 + * + * @param 返回值类型 + * @param key 锁的唯一标识符,用于区分不同的资源锁 + * @param timeout 获取锁的最大等待时间,单位为秒 + * @param supplier 需要在锁保护下执行的操作,提供返回值 + * @return 执行 supplier 后返回的结果 + */ + fun executeWithLock(key: String, timeout: Duration, supplier: () -> T): T + /** + * 执行带读锁的操作。 + * + * @param key 锁键 + * @param timeout 获取锁超时时间 + * @param supplier 要执行的读操作 + */ + fun executeWithReadLock(key: String, timeout: Duration, supplier: () -> T): T + + /** + * 执行带写锁的操作。 + * + * @param key 锁键 + * @param timeout 获取锁超时时间 + * @param supplier 要执行的写操作 + */ + fun executeWithWriteLock(key: String, timeout: Duration, supplier: () -> T): T +} diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/.gitattributes b/forgeboot-cache/forgeboot-cache-autoconfigure/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/.gitignore b/forgeboot-cache/forgeboot-cache-autoconfigure/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/build.gradle.kts b/forgeboot-cache/forgeboot-cache-autoconfigure/build.gradle.kts new file mode 100644 index 0000000..ec516c9 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + alias { libs.plugins.kotlin.plugin.spring } +} +dependencies { + compileOnly(project(Modules.Cache.API)) + compileOnly(project(Modules.Cache.IMPL)) + compileOnly(libs.springBoot.autoconfigure) + implementation(libs.springBootStarter.redis) + implementation(libs.jackson.databind) + implementation(libs.redisson.springBootStarter) + api(project(Modules.Core.SERIALIZATION)) +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/CacheAutoConfiguration.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/CacheAutoConfiguration.kt new file mode 100644 index 0000000..86869ae --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/CacheAutoConfiguration.kt @@ -0,0 +1,43 @@ +package com.gewuyou.forgeboot.cache.autoconfigure + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.autoconfigure.config.* +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Import + +/** + * 缓存自动配置类 + * + * 该类通过组合多个配置类实现完整的缓存功能装配,负责集成并协调以下模块: + * - Redis 基础设施:连接工厂与操作模板([CacheBaseConfig]) + * - 缓存策略定义:该配置类用于定义缓存相关的策略 Bean([CachePolicyConfig]) + * - 缓存实现层:基于 Redis 和 Caffeine 的两级缓存支持([CacheImplConfig]) + * - 多级缓存架构:支持本地缓存与分布式缓存的协同工作([CacheLayerConfig]) + * - 组合缓存配置:用于构建复合缓存操作逻辑([CacheCompositeConfig]) + * - 序列化管理:统一处理键值序列化与反序列化([CacheSerializerConfig]) + * - 缓存管理器:提供统一访问入口和运行时管理能力([CacheManagerConfig]) + * - 缓存预热机制:系统启动后自动加载热点数据([CacheWarmUpConfig]) + * - 缓存切面支持:为注解如 @CacheableEx, @CacheEvictEx 提供执行上下文([CacheAspectConfig]) + * - 分布式锁服务:提供跨节点资源同步机制([LockServiceConfig]) + * + * 所有导入的配置类共同构成完整的缓存解决方案,适用于高并发场景下的性能优化需求。 + * + * @property CacheProperties 提供外部可配置参数,用于定制缓存行为 + * + * @since 2025-06-17 20:49:17 + * @author gewuyou + */ +@Import( + CacheBaseConfig::class, + CachePolicyConfig::class, + CacheImplConfig::class, + CacheLayerConfig::class, + CacheCompositeConfig::class, + CacheSerializerConfig::class, + CacheManagerConfig::class, + CacheWarmUpConfig::class, + CacheAspectConfig::class, + LockServiceConfig::class +) +@EnableConfigurationProperties(CacheProperties::class) +class CacheAutoConfiguration \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheAspectConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheAspectConfig.kt new file mode 100644 index 0000000..dc25fdd --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheAspectConfig.kt @@ -0,0 +1,76 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.api.manager.CacheManager +import com.gewuyou.forgeboot.cache.impl.aspect.CacheEvictExAspect +import com.gewuyou.forgeboot.cache.impl.aspect.CachePutExAspect +import com.gewuyou.forgeboot.cache.impl.aspect.CacheableExAspect +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * 缓存切面配置类 + * + * 用于定义缓存相关的切面 Bean,包括 CacheableExAspect、CacheEvictExAspect 和 CachePutExAspect。 + * 这些切面处理基于 Spring AOP 的缓存操作逻辑。 + * + * @since 2025-06-21 11:41:51 + * @author gewuyou + */ +@Configuration +class CacheAspectConfig { + + /** + * 创建 CacheableExAspect 切面 Bean + * + * 该方法将 CacheManager 和 KeyGenerator 注入到 CacheableExAspect 实例中, + * 用于构建支持扩展的缓存获取切面逻辑。 + * + * @param cacheManager 缓存管理器,用于管理具体的缓存实现 + * @param keyGenerator 键生成器,用于根据方法参数生成缓存键 + * @return 初始化完成的 CacheableExAspect 实例 + */ + @Bean + fun cacheableExAspect( + cacheManager: CacheManager, + keyGenerator: KeyGenerator + ): CacheableExAspect { + return CacheableExAspect(cacheManager, keyGenerator) + } + + /** + * 创建 CacheEvictExAspect 切面 Bean + * + * 该方法将 CacheManager 和 KeyGenerator 注入到 CacheEvictExAspect 实例中, + * 用于构建支持扩展的缓存清除切面逻辑。 + * + * @param cacheManager 缓存管理器,用于管理具体的缓存实现 + * @param keyGenerator 键生成器,用于根据方法参数生成缓存键 + * @return 初始化完成的 CacheEvictExAspect 实例 + */ + @Bean + fun cacheEvictExAspect( + cacheManager: CacheManager, + keyGenerator: KeyGenerator + ): CacheEvictExAspect { + return CacheEvictExAspect(cacheManager, keyGenerator) + } + + /** + * 创建 CachePutExAspect 切面 Bean + * + * 该方法将 CacheManager 和 KeyGenerator 注入到 CachePutExAspect 实例中, + * 用于构建支持扩展的缓存更新切面逻辑。 + * + * @param cacheManager 缓存管理器,用于管理具体的缓存实现 + * @param keyGenerator 键生成器,用于根据方法参数生成缓存键 + * @return 初始化完成的 CachePutExAspect 实例 + */ + @Bean + fun cachePutExAspect( + cacheManager: CacheManager, + keyGenerator: KeyGenerator + ): CachePutExAspect { + return CachePutExAspect(cacheManager, keyGenerator) + } +} diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheBaseConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheBaseConfig.kt new file mode 100644 index 0000000..6955f4e --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheBaseConfig.kt @@ -0,0 +1,73 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.impl.generator.DefaultKeyGenerator +import com.gewuyou.forgeboot.cache.impl.utils.RedisKeyScanner +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.connection.RedisConnectionFactory +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory +import org.springframework.data.redis.core.StringRedisTemplate + +/** + * 缓存基础配置类,用于定义缓存相关的基础 Bean。 + * + * @since 2025-06-20 23:31:09 + * @author gewuyou + */ +@Configuration(proxyBeanMethods = false) +class CacheBaseConfig { + /** + * 创建 StringRedisTemplate Bean。 + * + * StringRedisTemplate 是 Spring Data Redis 提供的模板类, + * 专门用于操作 Redis 中的字符串类型数据。 + * + * @return 配置好的 StringRedisTemplate 实例 + */ + @Bean + fun redisTemplate(): StringRedisTemplate { + return StringRedisTemplate() + } + + /** + * 创建 RedisConnectionFactory Bean。 + * + * RedisConnectionFactory 是连接 Redis 数据库的基础组件, + * 使用 Lettuce 客户端实现连接。 + * + * @return 配置好的 LettuceConnectionFactory 实例 + */ + @Bean + fun redisConnectionFactory(): RedisConnectionFactory { + return LettuceConnectionFactory() + } + + /** + * 创建 RedisKeyScanner Bean。 + * + * RedisKeyScanner 用于扫描 Redis 中的键(Key),便于管理与维护缓存数据。 + * + * @param redisConnectionFactory 已注入的 Redis 连接工厂实例 + * @return 配置好的 RedisKeyScanner 实例 + */ + @Bean + fun redisKeyScanner(redisConnectionFactory: RedisConnectionFactory): RedisKeyScanner { + return RedisKeyScanner(redisConnectionFactory) + } + + /** + * 创建默认的 KeyGenerator Bean。 + * + * DefaultKeyGenerator 用于生成统一格式的缓存键,确保缓存键的命名一致性。 + * 若上下文中不存在 KeyGenerator 类型的 Bean,则自动创建此默认实例。 + * + * @return 默认的 KeyGenerator 实现实例 + */ + @Bean + @ConditionalOnMissingBean + fun keyGenerator(): KeyGenerator { + return DefaultKeyGenerator() + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheCompositeConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheCompositeConfig.kt new file mode 100644 index 0000000..d7f1e80 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheCompositeConfig.kt @@ -0,0 +1,41 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.entities.CacheLayer +import com.gewuyou.forgeboot.cache.impl.contract.CompositeCache +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary + +/** + * 缓存复合配置类 + * + * 该配置类用于定义与缓存相关的主缓存 Bean,通过组合多层缓存实现灵活的缓存策略。 + * 主要职责是创建并初始化 CompositeCache 实例作为 Spring 容器中的核心缓存组件。 + * + * @since 2025-06-21 11:40:10 + * @author gewuyou + */ +@Configuration +class CacheCompositeConfig { + + /** + * 创建主缓存 Bean,使用 CompositeCache 组合多层缓存 + * + * 该方法将 cacheLayers 和 cacheProperties 注入到 CompositeCache 实例中, + * 构建出一个支持多层缓存结构的主缓存组件。该组件会被 Spring 标记为首选 Bean。 + * + * @param cacheLayers 缓存层列表,包含多个 CacheLayer 实例,用于构建缓存层级结构 + * @param cacheProperties 缓存配置对象,包含全局缓存相关属性设置 + * @return 返回构建完成的 Cache 实例,实际类型为 CompositeCache + */ + @Bean("compositeCache") + @Primary + fun compositeCache( + cacheLayers: List, + cacheProperties: CacheProperties, + ): Cache { + return CompositeCache(cacheLayers, cacheProperties) + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheImplConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheImplConfig.kt new file mode 100644 index 0000000..ac2750d --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheImplConfig.kt @@ -0,0 +1,65 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.api.policy.CachePolicy +import com.gewuyou.forgeboot.cache.api.policy.NullValuePolicy +import com.gewuyou.forgeboot.cache.impl.contract.MultiPolicyCache +import com.gewuyou.forgeboot.cache.impl.contract.PerEntryTtlCaffeineCache +import com.gewuyou.forgeboot.cache.impl.contract.RedisCache +import com.gewuyou.forgeboot.cache.impl.utils.RedisKeyScanner +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.core.StringRedisTemplate + +/** + * 缓存实现配置类 + * + * 该配置类定义了缓存相关的 Bean,包括基于 Redis 和 Caffeine 的 NullAwareCache 实例。 + * + * @since 2025-06-21 11:38:18 + * @author gewuyou + */ +@Configuration +class CacheImplConfig { + + /** + * 构建基于 Redis 的 NullAwareCache 缓存实例 + * + * 该方法创建了一个包装 RedisCache 的 NullAwareCache 实例,并结合 NullValuePolicy 控制 null 值的处理策略。 + * + * @param redisTemplate Redis 模板,用于操作 Redis 数据库 + * @param redisKeyScanner 用于扫描 Redis 中的键 + * @param keyGenerator 用于生成缓存键 + * @param nullCachePolicy NullValuePolicy 实例,控制 null 值的缓存行为 + * @return 返回一个基于 Redis 的 Cache 实例 + */ + @Bean("redisNullAwareCache") + fun redisNullAwareCache( + redisTemplate: StringRedisTemplate, + redisKeyScanner: RedisKeyScanner, + keyGenerator: KeyGenerator, + nullCachePolicy: NullValuePolicy, + policies: List + ): Cache { + return MultiPolicyCache(RedisCache(redisTemplate, redisKeyScanner, keyGenerator), nullCachePolicy,policies) + } + + /** + * 构建基于 Caffeine 的 NullAwareCache 缓存实例 + * + * 该方法创建了一个使用 PerEntryTtlCaffeineCache 的 NullAwareCache 实例,支持条目级别的 TTL(生存时间)设置。 + * + * @param keyGenerator 用于生成缓存键 + * @param nullCachePolicy NullValuePolicy 实例,控制 null 值的缓存行为 + * @return 返回一个基于 Caffeine 的 Cache 实例 + */ + @Bean("perEntryTtlCaffeineNullAwareCache") + fun perEntryTtlCaffeineNullAwareCache( + keyGenerator: KeyGenerator, + nullCachePolicy: NullValuePolicy, + policies: List + ): Cache { + return MultiPolicyCache(PerEntryTtlCaffeineCache(keyGenerator), nullCachePolicy,policies) + } +} diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheLayerConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheLayerConfig.kt new file mode 100644 index 0000000..ee590ce --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheLayerConfig.kt @@ -0,0 +1,43 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.entities.CacheLayer +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * 缓存层配置 + * + * 该配置类用于定义多级缓存的层级结构,当前实现中包含两级缓存: + * 第一层为基于 Caffeine 的本地缓存,具有每个条目独立的生存时间(TTL)特性; + * 第二层为基于 Redis 的分布式缓存,用于跨服务共享缓存数据。 + * + * @since 2025-06-21 11:39:35 + * @author gewuyou + */ +@Configuration +class CacheLayerConfig { + + /** + * 定义缓存层级结构,Caffeine 缓存在第一层,Redis 缓存在第二层 + * + * 该方法创建并返回一个包含两个缓存层的列表。每层缓存都与一个具体的缓存实现绑定, + * 并指定其在整体架构中的优先级顺序。第一层使用本地的 Caffeine 缓存以提高访问速度, + * 第二层使用 Redis 缓存来保证数据的共享和持久性。 + * + * @param redisNullAwareCache 第二层缓存,基于 Redis 的 Null-aware 缓存实现 + * @param perEntryTtlCaffeineNullAwareCache 第一层缓存,基于 Caffeine 的 Null-aware 缓存实现,支持每个缓存条目独立的 TTL 设置 + * @return 包含两个缓存层的列表,按优先级排序 + */ + @Bean + fun cacheLayers( + @Qualifier("redisNullAwareCache") redisNullAwareCache: Cache, + @Qualifier("perEntryTtlCaffeineNullAwareCache") perEntryTtlCaffeineNullAwareCache: Cache, + ): List { + return listOf( + CacheLayer(perEntryTtlCaffeineNullAwareCache, 1), // 第一层:Caffeine 缓存,速度快,适合高频访问数据 + CacheLayer(redisNullAwareCache, 2) // 第二层:Redis 缓存,用于持久化存储和跨节点共享数据 + ) + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheManagerConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheManagerConfig.kt new file mode 100644 index 0000000..058dc00 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheManagerConfig.kt @@ -0,0 +1,57 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.api.manager.CacheManager +import com.gewuyou.forgeboot.cache.impl.manager.DefaultCacheManager +import com.gewuyou.forgeboot.core.serialization.serializer.ValueSerializer +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * 缓存管理器配置 + * + * 配置类用于定义缓存管理相关的 Bean,主要职责是创建和初始化 CacheManager 实例。 + * 该配置类基于 Spring 的 @Configuration 注解标识为配置类,并通过 @Bean 定义核心 Bean。 + * + * @since 2025-06-21 11:41:05 + * @author gewuyou + */ +@Configuration +class CacheManagerConfig { + + /** + * 创建并配置 CacheManager 缓存管理器 Bean。 + * + * 此方法负责实例化一个 DefaultCacheManager,它依赖以下组件: + * - cacheProperties:提供缓存的全局配置参数。 + * - cache:实现缓存数据存储与访问的核心接口。 + * - serializer:用于序列化和反序列化缓存值。 + * - keyGenerator:生成缓存键的策略接口。 + * + * 返回的 CacheManager 是一个具体实现,用于管理整个应用中的缓存操作。 + * + * @param cacheProperties 缓存配置信息 + * @param cache 缓存存储与访问的具体实现 + * @param serializer 序列化/反序列化工具 + * @param keyGenerator 缓存键生成策略 + * @return 初始化完成的缓存管理器实例 + */ + @Bean + @ConditionalOnMissingBean + fun cacheManager( + cacheProperties: CacheProperties, + cache: Cache, + serializer: ValueSerializer, + keyGenerator: KeyGenerator + ): CacheManager { + return DefaultCacheManager( + cacheProperties, + cache, + serializer, + keyGenerator + ) + } +} diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CachePolicyConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CachePolicyConfig.kt new file mode 100644 index 0000000..26943dd --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CachePolicyConfig.kt @@ -0,0 +1,53 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.api.policy.CachePolicy +import com.gewuyou.forgeboot.cache.api.policy.NullValuePolicy +import com.gewuyou.forgeboot.cache.impl.policy.AllowNullCachePolicy +import com.gewuyou.forgeboot.cache.impl.policy.NullValueCachePolicy +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * 缓存策略配置类 + * + * 该配置类用于定义缓存相关的策略 Bean,当前主要包含 NullValuePolicy 的默认实现。 + * 所有需要注册为 Spring Bean 的缓存策略应在本类中进行声明和配置。 + * + * @since 2025-06-21 11:37:50 + * @author gewuyou + */ +@Configuration +class CachePolicyConfig { + /** + * 创建 NullValuePolicy 类型的 Bean,用于控制是否允许缓存 null 值。 + * + * 当前使用 AllowNullCachePolicy 作为默认实现,表示允许缓存 null 值。 + * 如果应用上下文中已存在同类型的 Bean,则跳过此 Bean 的创建。 + * + * @return 返回一个 NullValuePolicy 接口的实现对象 + */ + @Bean + @ConditionalOnMissingBean + fun nullCachePolicy(cacheProperties: CacheProperties): NullValuePolicy { + return AllowNullCachePolicy(cacheProperties) + } + + /** + * 创建 CachePolicy 类型的 Bean,用于包装基础的 NullValuePolicy 实现。 + * + * 该方法将 NullValuePolicy 实例封装为一个 CachePolicy 接口的实现对象, + * 允许在缓存操作中应用 null 值策略。如果应用上下文中已存在同类型的 Bean, + * 则跳过此 Bean 的创建。 + * + * @param nullValuePolicy 注入的 NullValuePolicy 实例,用于决定是否允许缓存 null 值 + * @return 返回封装后的 CachePolicy 对象 + */ + @Bean("nullValueCachePolicy") + @ConditionalOnMissingBean + fun nullValueCachePolicy(nullValuePolicy: NullValuePolicy): CachePolicy { + return NullValueCachePolicy(nullValuePolicy) + } + +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheSerializerConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheSerializerConfig.kt new file mode 100644 index 0000000..0abb80c --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheSerializerConfig.kt @@ -0,0 +1,36 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.fasterxml.jackson.databind.ObjectMapper +import com.gewuyou.forgeboot.core.serialization.serializer.ValueSerializer +import com.gewuyou.forgeboot.core.serialization.serializer.impl.serializer.JacksonValueSerializer + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * 缓存序列化程序配置 + * + * 该配置类用于定义缓存所需的序列化器 Bean。 + * + * @since 2025-06-21 11:40:45 + * @author gewuyou + */ +@Configuration +class CacheSerializerConfig { + + /** + * 创建 JacksonValueSerializer 序列化器 Bean + * + * 该方法定义了一个 JacksonValueSerializer 的 Bean,用于将值序列化和反序列化, + * 适用于与 Jackson 库集成进行 JSON 数据格式的处理。 + * + * @param objectMapper Jackson 提供的对象映射器,用于实际的序列化/反序列化操作 + * @return 返回一个 JacksonValueSerializer 实例,作为 ValueSerializer 接口的实现 + */ + @Bean + @ConditionalOnMissingBean + fun serializer(objectMapper: ObjectMapper): ValueSerializer { + return JacksonValueSerializer(objectMapper) + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheWarmUpConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheWarmUpConfig.kt new file mode 100644 index 0000000..13cfb67 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/CacheWarmUpConfig.kt @@ -0,0 +1,34 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.service.CacheWarmUpService +import com.gewuyou.forgeboot.cache.impl.runner.CacheWarmUpRunner +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * 缓存预热配置类 + * + * 该配置类用于定义与缓存预热相关的 Bean,确保应用启动时能够自动执行缓存预热逻辑。 + * + * @since 2025-06-21 11:41:34 + * @author gewuyou + */ +@Configuration +class CacheWarmUpConfig { + + /** + * 创建 CacheWarmUpRunner Bean + * + * 该方法将一组 CacheWarmUpService 实例注入到 CacheWarmUpRunner 中, + * 用于在应用启动时运行缓存预热任务。 + * + * @param services 缓存预热服务的列表,每个服务实现具体的预热逻辑 + * @return 返回一个 CacheWarmUpRunner 实例,用于启动缓存预热流程 + */ + @Bean + fun cacheWarmUpRunner( + services: List + ): CacheWarmUpRunner { + return CacheWarmUpRunner(services) + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/LockServiceConfig.kt b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/LockServiceConfig.kt new file mode 100644 index 0000000..d7d582b --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/cache/autoconfigure/config/LockServiceConfig.kt @@ -0,0 +1,35 @@ +package com.gewuyou.forgeboot.cache.autoconfigure.config + +import com.gewuyou.forgeboot.cache.api.service.LockService +import com.gewuyou.forgeboot.cache.impl.service.RedisLockService +import org.redisson.api.RedissonClient +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * 锁服务配置类 + * + * 该配置类用于定义与锁服务相关的 Bean,确保应用中可以正常使用分布式锁功能。 + * + * @since 2025-06-21 11:42:20 + * @author gewuyou + */ +@Configuration +class LockServiceConfig { + + /** + * 创建 LockService 锁服务 Bean + * + * 该方法定义了一个 LockService 类型的 Bean,用于提供基于 Redisson 的分布式锁实现。 + * 如果 Spring 容器中尚未存在 LockService 实例,则会通过此方法创建一个。 + * + * @param redissonClient Redisson 客户端实例,用于与 Redis 进行交互并创建锁 + * @return 返回一个 LockService 实例,具体实现为 RedisLockService + */ + @Bean + @ConditionalOnMissingBean + fun lockService(redissonClient: RedissonClient): LockService { + return RedisLockService(redissonClient) + } +} diff --git a/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..bcc928b --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.gewuyou.forgeboot.cache.autoconfigure.CacheAutoConfiguration diff --git a/forgeboot-cache/forgeboot-cache-impl/.gitattributes b/forgeboot-cache/forgeboot-cache-impl/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-cache/forgeboot-cache-impl/.gitignore b/forgeboot-cache/forgeboot-cache-impl/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-cache/forgeboot-cache-impl/build.gradle.kts b/forgeboot-cache/forgeboot-cache-impl/build.gradle.kts new file mode 100644 index 0000000..3a04a08 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/build.gradle.kts @@ -0,0 +1,13 @@ +plugins{ + alias(libs.plugins.kotlin.plugin.spring) +} +dependencies { + compileOnly(project(Modules.Cache.API)) + compileOnly(project(Modules.Core.SERIALIZATION)) + implementation(libs.springBootStarter.aop) + implementation(libs.springBootStarter.redis) + implementation(libs.com.github.benManes.caffeine) + implementation(libs.kotlinxCoroutines.core) + implementation(libs.jackson.databind) + implementation(libs.redisson.springBootStarter) +} diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CacheEvictExAspect.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CacheEvictExAspect.kt new file mode 100644 index 0000000..c93405a --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CacheEvictExAspect.kt @@ -0,0 +1,50 @@ +package com.gewuyou.forgeboot.cache.impl.aspect + +import com.gewuyou.forgeboot.cache.api.annotations.CacheEvictEx +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.api.manager.CacheManager +import com.gewuyou.forgeboot.cache.impl.support.CacheSpELHelper +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.annotation.After +import org.aspectj.lang.annotation.Aspect + +/** + * 缓存清理扩展切面 + * + * 该切面用于处理带有 [CacheEvictEx] 注解的方法,在方法执行后清除指定的缓存项。 + * + * @property cacheManager 用于管理缓存的组件,提供缓存操作的接口。 + * @property keyGenerator 用于生成缓存键的辅助组件,支持动态键值解析。 + * + * @since 2025-06-18 20:59:18 + * @author gewuyou + */ +@Aspect +class CacheEvictExAspect( + private val cacheManager: CacheManager, + private val keyGenerator: KeyGenerator +) { + + /** + * 处理被 [CacheEvictEx] 注解标记的方法调用后的缓存清理逻辑。 + * + * 该方法会在目标方法执行完成后触发,根据注解配置的命名空间和 SpEL 表达式 + * 构建完整的缓存键,并通过 [cacheManager] 移除对应的缓存项。 + * + * @param joinPoint 封装了目标方法调用上下文的信息,包括方法参数、返回值等。 + * @param cacheEvictEx 注解实例,包含缓存清理所需的配置信息。 + */ + @After("@annotation(cacheEvictEx)") + fun handle(joinPoint: JoinPoint, cacheEvictEx: CacheEvictEx) { + val namespace = cacheEvictEx.namespace + // 解析并构建完整的缓存键 + val fullKey = CacheSpELHelper.parseKey( + namespace, + cacheEvictEx.keySpEL, + joinPoint, + keyGenerator + ) + // 执行缓存项移除操作 + cacheManager.getCache(namespace).remove(fullKey) + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CachePutExAspect.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CachePutExAspect.kt new file mode 100644 index 0000000..051ed85 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CachePutExAspect.kt @@ -0,0 +1,55 @@ +package com.gewuyou.forgeboot.cache.impl.aspect + +import com.gewuyou.forgeboot.cache.api.annotations.CachePutEx +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.api.manager.CacheManager +import com.gewuyou.forgeboot.cache.impl.support.CacheSpELHelper +import com.gewuyou.forgeboot.cache.impl.utils.SpELResolver +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.annotation.AfterReturning +import org.aspectj.lang.annotation.Aspect +import java.time.Duration + +/** + * 缓存插入扩展切面 + * + * 该切面用于处理带有 [CachePutEx] 注解的方法,将方法执行结果根据指定的命名空间和键存储到缓存中,并设置过期时间。 + * + * @property cacheManager 缓存管理器,用于获取缓存服务实例。 + * @property keyGenerator 缓存键生成器,用于生成完整的缓存键。 + * + * @since 2025-06-18 20:57:27 + * @author gewuyou + */ +@Aspect +class CachePutExAspect( + private val cacheManager: CacheManager, + private val keyGenerator: KeyGenerator +) { + + /** + * 处理带有 [CachePutEx] 注解的方法返回后逻辑。 + * + * 通过 AOP 获取方法参数、注解配置的命名空间、键表达式和过期时间, + * 使用 [SpELResolver] 解析出最终的缓存键,并调用 [cacheManager] 将结果写入缓存。 + * + * @param joinPoint 切点信息,包含目标方法及其参数。 + * @param cachePutEx 方法上的 [CachePutEx] 注解实例,定义缓存行为配置。 + * @param result 方法执行后的返回值。 + */ + @AfterReturning("@annotation(cachePutEx)", returning = "result") + fun handle(joinPoint: JoinPoint, cachePutEx: CachePutEx, result: Any?) { + if (result == null) return + val namespace = cachePutEx.namespace + // 解析缓存键 + val fullKey = CacheSpELHelper.parseKey( + namespace, + cachePutEx.keySpEL, + joinPoint, + keyGenerator + ) + // 获取缓存过期时间(单位:秒) + val ttl = cachePutEx.ttl + cacheManager.getCache(namespace).put(fullKey, result, Duration.ofSeconds(ttl)) + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CacheableExAspect.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CacheableExAspect.kt new file mode 100644 index 0000000..580e71d --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/aspect/CacheableExAspect.kt @@ -0,0 +1,76 @@ +package com.gewuyou.forgeboot.cache.impl.aspect + +import com.gewuyou.forgeboot.cache.api.annotations.CacheableEx +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.api.manager.CacheManager +import com.gewuyou.forgeboot.cache.impl.utils.SpELResolver +import org.aspectj.lang.ProceedingJoinPoint +import org.aspectj.lang.annotation.Around +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.reflect.MethodSignature +import java.time.Duration + +/** + * 可缓存的扩展切面类 + * + * 用于处理带有 @CacheableEx 注解的方法,实现方法结果的缓存逻辑。 + * 通过 AOP 拦截注解方法,根据配置生成缓存键并操作缓存服务。 + * + * @property cacheManager 缓存管理器,用于操作缓存服务 + * @property keyGenerator 缓存键生成器,用于生成完整的缓存键 + */ +@Aspect +class CacheableExAspect( + private val cacheManager: CacheManager, + private val keyGenerator: KeyGenerator +) { + + /** + * 环绕通知方法,处理缓存逻辑 + * + * 执行流程: + * 1. 解析方法参数并生成参数映射表 + * 2. 使用 SpEL 表达式解析器生成缓存键 + * 3. 从缓存中尝试获取已存在的值 + * 4. 如果缓存命中且值不为 null(或允许缓存 null 值),则直接返回缓存结果 + * 5. 否则执行目标方法,并将结果写入缓存 + * + * @param joinPoint 切点信息,包含目标方法及参数等 + * @param cacheableEx 方法上的 @CacheableEx 注解实例,提供缓存配置 + * @return 目标方法的执行结果,可能是缓存值也可能是实际调用结果 + */ + @Around("@annotation(cacheableEx)") + fun handle(joinPoint: ProceedingJoinPoint, cacheableEx: CacheableEx): Any? { + // 提取目标方法对象 + val method = (joinPoint.signature as MethodSignature).method + + // 构建参数名称与值的映射关系 + val argsMap = method.parameters.mapIndexed { i, param -> param.name to joinPoint.args[i] }.toMap() + + // 使用 SpEL 解析表达式生成缓存键 + val key = SpELResolver.parse(cacheableEx.keySpEL, argsMap) + + // 获取缓存值类型、命名空间和过期时间 + val type = cacheableEx.type.java + val namespace = cacheableEx.namespace + val ttl = cacheableEx.ttl + + // 生成完整缓存键 + val fullKey = keyGenerator.generateKey(namespace, key) + val cacheService = cacheManager.getCache(namespace) + // 尝试从缓存获取数据 + val cached = cacheService.retrieve(fullKey, type) + + if (cached != null) return cached + + // 执行原始方法逻辑 + val result = joinPoint.proceed() + + // 根据是否允许缓存 null 值进行判断 + if (result == null && !cacheableEx.cacheNull) return null + + // 将非 null 结果写入缓存,空值使用占位符 "" + cacheService.put(fullKey, result ?: "", Duration.ofSeconds(ttl)) + return result + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/CompositeCache.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/CompositeCache.kt new file mode 100644 index 0000000..c535b5b --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/CompositeCache.kt @@ -0,0 +1,91 @@ +package com.gewuyou.forgeboot.cache.impl.contract + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.entities.CacheLayer +import java.time.Duration + +/** + * 复合缓存实现类,通过组合多个缓存层提供统一的缓存访问接口。 + * + * 缓存按照优先级排序,高优先级的缓存层在前。读取时会从高到低依次查找, + * 一旦在某一层找到数据,就会将该数据回填到优先级更高的缓存层中。 + * + * @property layers 缓存层列表,每个缓存层包含一个具体的缓存实例和优先级。 + * @property cacheProperties 配置属性对象,用于获取默认缓存过期时间等配置参数。 + * @constructor 创建一个复合缓存实例,并按优先级对缓存层进行排序。 + */ +class CompositeCache( + layers: List, + private val cacheProperties: CacheProperties +) : Cache { + + /** + * 按照优先级排序后的缓存层列表。 + * 高优先级的缓存层排在前面,用于后续的缓存查找与回填逻辑。 + */ + private val sortedLayers = layers.sortedBy { it.priority } + + /** + * 获取指定键对应的缓存值。 + * + * 从最高优先级的缓存层开始查找,一旦找到有效值,则将其回填到 + * 所有优先级高于当前层的缓存中,并返回该值。 + * + * @param key 要获取的缓存键。 + * @return 如果存在缓存值则返回对应值,否则返回 null。 + */ + override fun retrieve(key: String): String? { + for ((index, layer) in sortedLayers.withIndex()) { + val value = layer.cache.retrieve(key) + if (value != null) { + // 将找到的值回填到所有更高优先级的缓存层中 + for (i in 0 until index) { + sortedLayers[i].cache[key, value] = cacheProperties.theDefaultCacheTTL + } + return value + } + } + return null + } + + /** + * 将指定键值对放入所有缓存层中。 + * + * @param key 要存储的缓存键。 + * @param value 要存储的缓存值。 + * @param ttl 存活时间。 + */ + override fun put(key: String, value: String?, ttl: Duration) { + sortedLayers.forEach { it.cache[key, value] = ttl } + } + + /** + * 从所有缓存层中移除指定键的缓存项。 + * + * @param key 要移除的缓存键。 + * @return 如果所有缓存层都成功移除了该键则返回 true,否则返回 false。 + */ + override fun remove(key: String): Boolean { + return sortedLayers.all { it.cache.remove(key) } + } + + /** + * 判断指定键是否存在于任意一个缓存层中。 + * + * @param key 要检查的缓存键。 + * @return 如果存在任意一层缓存包含该键则返回 true,否则返回 false。 + */ + override fun exists(key: String): Boolean { + return sortedLayers.any { it.cache.exists(key) } + } + + /** + * 清空缓存中的所有数据。 + * + * @param namespace 命名空间,用于限定清空操作的作用域。 + */ + override fun clear(namespace: String) { + sortedLayers.forEach { it.cache.clear(namespace) } + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/GlobalTtlCaffeineCache.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/GlobalTtlCaffeineCache.kt new file mode 100644 index 0000000..d2182f5 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/GlobalTtlCaffeineCache.kt @@ -0,0 +1,88 @@ +package com.gewuyou.forgeboot.cache.impl.contract + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.github.benmanes.caffeine.cache.Caffeine +import java.time.Duration +import java.util.concurrent.TimeUnit + +/** + * 全局 TTL 咖啡因缓存 + * + * 该实现基于 Caffeine 缓存库,所有缓存项使用统一的默认过期时间(TTL)。 + * 不支持针对单个缓存项设置不同的 TTL。 + * + * @property cacheProperties 缓存配置属性,用于获取默认 TTL 设置 + * @property keyGenerator 键生成器,用于构建带命名空间连接符的键前缀 + * @since 2025-06-17 22:32:36 + * @author gewuyou + */ +class GlobalTtlCaffeineCache( + cacheProperties: CacheProperties, + private val keyGenerator: KeyGenerator +) : Cache { + + /** + * 初始化 Caffeine 缓存实例 + * + * 使用 expireAfterWrite 设置写入后过期时间,时间长度基于 cacheProperties.theDefaultCacheTTL 配置。 + * 最大缓存条目数量限制为 10,000。 + */ + private val cache = Caffeine.newBuilder() + .expireAfterWrite(cacheProperties.theDefaultCacheTTL.toMillis(), TimeUnit.MILLISECONDS) + .maximumSize(10_000) + .build() + + /** + * 获取指定 key 对应的缓存值 + * + * @param key 缓存键 + * @return 缓存中对应的值,如果不存在则返回 null + */ + override fun retrieve(key: String): String? = cache.getIfPresent(key) + + /** + * 将键值对放入缓存中 + * + * 注意:此处忽略传入的 ttl 参数,统一使用 defaultTtl 控制过期时间。 + * + * @param key 缓存键 + * @param value 缓存值,若为 null 则不存储 + * @param ttl 忽略此参数,使用全局默认的过期时间 + */ + override fun put(key: String, value: String?, ttl: Duration) { + if (value != null) cache.put(key, value) + } + + /** + * 移除指定 key 的缓存项 + * + * @param key 要移除的缓存键 + * @return 总是返回 true,表示操作成功 + */ + override fun remove(key: String): Boolean { + cache.invalidate(key) + return true + } + + /** + * 检查指定 key 是否存在于缓存中 + * + * @param key 缓存键 + * @return 如果存在则返回 true,否则返回 false + */ + override fun exists(key: String): Boolean = cache.getIfPresent(key) != null + + /** + * 清除指定命名空间下的所有缓存项 + * + * 根据命名空间和连接符拼接出前缀,并清除所有以前缀开头的 key。 + * + * @param namespace 命名空间前缀 + */ + override fun clear(namespace: String) { + val prefix = "$namespace${keyGenerator.getConnectors()}" + cache.asMap().keys.filter { it.startsWith(prefix) }.forEach { cache.invalidate(it) } + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/MultiPolicyCache.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/MultiPolicyCache.kt new file mode 100644 index 0000000..282611d --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/MultiPolicyCache.kt @@ -0,0 +1,103 @@ +package com.gewuyou.forgeboot.cache.impl.contract + +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.policy.CachePolicy +import com.gewuyou.forgeboot.cache.api.policy.NullValuePolicy +import java.time.Duration + +/** + * 多策略缓存装饰器 + * + * 支持多个缓存策略,动态应用各种缓存策略(如 Null 值策略、失效策略等)。 + * 该类通过组合多个 CachePolicy 实现对基础缓存操作的增强处理。 + * + * @property delegate 被包装的真实缓存实现,用于执行底层缓存操作 + * @property policies 缓存策略列表,按顺序依次应用于缓存值 + * @since 2025-06-17 21:16:58 + */ +class MultiPolicyCache( + private val delegate: Cache, + private val nullValuePolicy: NullValuePolicy, + private val policies: List, +) : Cache { + + init { + // 排序 + policies.sortedBy { it.getOrder() } + } + + /** + * 获取缓存值并应用所有缓存策略 + * + * 按照策略列表顺序依次处理获取到的缓存值: + * - 如果原始值为 null 或 null 占位符,则返回 null + * - 否则返回经过策略处理后的最终值 + * + * @param key 缓存键,用于定位缓存项 + * @return 经过策略处理后的缓存值,如果为 null 或 null 占位符则返回 null + */ + override fun retrieve(key: String): String? { + var value = delegate.retrieve(key) + // 按策略顺序依次处理缓存值 + policies.forEach { value = it.apply(key, value) } + return value + } + + /** + * 存储缓存值并应用所有缓存策略 + * + * 按照策略列表顺序依次处理要存储的值: + * - 如果值为 null,可能被转换为 null 占位符 + * - 最终将处理后的值委托给底层缓存实现进行存储 + * + * @param key 缓存键,用于标识要存储的缓存项 + * @param value 要存储的值,可能为 null + * @param ttl 缓存过期时间,控制该项在缓存中的存活时长 + */ + override fun put(key: String, value: String?, ttl: Duration) { + var processedValue = value + // 按策略顺序依次处理待存储的值 + policies.forEach { processedValue = it.apply(key, processedValue) } + // 如果最终值是 null 且不允许缓存 null,跳过缓存存储 + if (processedValue == null && !nullValuePolicy.allowCacheNull(key)) { + // 不缓存 null 值 + return + } + delegate.put(key, processedValue, ttl) + } + + /** + * 移除指定缓存项 + * + * 将移除操作直接委托给底层缓存实现。 + * + * @param key 要移除的缓存键 + * @return 移除成功返回 true,否则返回 false + */ + override fun remove(key: String): Boolean { + return delegate.remove(key) + } + + /** + * 检查指定缓存项是否存在 + * + * 将存在性检查操作直接委托给底层缓存实现。 + * + * @param key 要检查的缓存键 + * @return 存在返回 true,否则返回 false + */ + override fun exists(key: String): Boolean { + return delegate.exists(key) + } + + /** + * 清除指定命名空间下的所有缓存 + * + * 将清除操作直接委托给底层缓存实现。 + * + * @param namespace 要清除的命名空间 + */ + override fun clear(namespace: String) { + delegate.clear(namespace) + } +} diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/PerEntryTtlCaffeineCache.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/PerEntryTtlCaffeineCache.kt new file mode 100644 index 0000000..2bdf829 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/PerEntryTtlCaffeineCache.kt @@ -0,0 +1,114 @@ +package com.gewuyou.forgeboot.cache.impl.contract + +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import kotlinx.coroutines.* +import java.time.Duration +import java.util.concurrent.ConcurrentHashMap +import kotlin.time.Duration.Companion.seconds + +/** + * 每个条目 TTL 咖啡因缓存实现类 + * + * 该缓存为每个缓存条目设置独立的生存时间(TTL),基于 Caffeine 缓存策略设计。 + * + * @since 2025-06-17 22:34:50 + * @author gewuyou + */ +class PerEntryTtlCaffeineCache( + private val keyGenerator: KeyGenerator +) : Cache { + + /** + * 缓存条目数据类,用于存储值和过期时间 + * + * @property value 缓存的字符串值 + * @property expireAt 条目的过期时间戳(毫秒) + */ + private data class CacheEntry(val value: String, val expireAt: Long) + + /** + * 使用线程安全的 ConcurrentHashMap 存储缓存条目 + */ + private val map = ConcurrentHashMap(16) + + /** + * 协程作用域,用于执行后台清理任务 + */ + private val cleanupScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) + + /** + * 初始化块:启动定期清理任务 + * + * 清理任务每 5 秒运行一次,移除所有已过期的缓存条目。 + */ + init { + cleanupScope.launch { + while (this.isActive) { + val now = System.currentTimeMillis() + map.entries.removeIf { it.value.expireAt <= now } + delay(5.seconds) + } + } + } + + /** + * 获取指定键的缓存值 + * + * 如果缓存不存在或已过期,则返回 null 并从缓存中移除该条目。 + * + * @param key 要获取的缓存键 + * @return 缓存值(如果存在且未过期),否则 null + */ + override fun retrieve(key: String): String? { + val entry = map[key] ?: return null + return if (System.currentTimeMillis() > entry.expireAt) { + map.remove(key) + null + } else entry.value + } + + /** + * 将键值对放入缓存,并设置其生存时间 + * + * 如果值为 null,则不会插入任何内容。 + * + * @param key 键 + * @param value 值 + * @param ttl 生存时间(Duration 类型) + */ + override fun put(key: String, value: String?, ttl: Duration) { + if (value != null) { + val expireAt = System.currentTimeMillis() + ttl.toMillis() + map[key] = CacheEntry(value, expireAt) + } + } + + /** + * 从缓存中移除指定键 + * + * @param key 要移除的键 + * @return 如果成功移除则返回 true,否则 false + */ + override fun remove(key: String): Boolean = map.remove(key) != null + + /** + * 检查指定键是否存在有效的缓存条目 + * + * @param key 要检查的键 + * @return 如果存在有效条目则返回 true,否则 false + */ + override fun exists(key: String): Boolean = retrieve(key) != null + + /** + * 清除指定命名空间下的所有缓存条目 + * + * 根据命名空间和连接符生成前缀,移除所有以该前缀开头的键。 + * + * @param namespace 命名空间 + */ + override fun clear(namespace: String) { + val prefix = "$namespace${keyGenerator.getConnectors()}" + map.keys.removeIf { it.startsWith(prefix) } + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/RedisCache.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/RedisCache.kt new file mode 100644 index 0000000..e0a624d --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/contract/RedisCache.kt @@ -0,0 +1,80 @@ +package com.gewuyou.forgeboot.cache.impl.contract + +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.impl.utils.RedisKeyScanner +import org.springframework.data.redis.core.StringRedisTemplate +import java.time.Duration + +/** + * Redis 缓存实现类,用于通过 Redis 存储、获取和管理缓存数据。 + * + * @property redisTemplate Spring 提供的 StringRedisTemplate 实例,用于与 Redis 进行交互 + * @property redisKeyScanner 用于扫描匹配的 Redis 键集合 + * @author gewuyou + * @since 2025-06-17 21:08:36 + */ +class RedisCache( + private val redisTemplate: StringRedisTemplate, + private val redisKeyScanner: RedisKeyScanner, + private val keyGenerator: KeyGenerator +) : Cache { + /** + * 获取指定键对应的缓存值。 + * + * @param key 缓存键,非空字符串 + * @return 缓存值,如果不存在则返回 null + */ + override fun retrieve(key: String): String? { + return redisTemplate.opsForValue()[key] + } + + /** + * 将键值对存储到缓存中,并设置过期时间。 + * + * @param key 缓存键,非空字符串 + * @param value 缓存值,可以为 null + * @param ttl 缓存存活时间(Time To Live),单位为秒或毫秒等 + */ + override fun put(key: String, value: String?, ttl: Duration) { + redisTemplate.opsForValue()[key, value!!] = ttl + } + + /** + * 删除指定键的缓存。 + * + * @param key 要删除的缓存键,非空字符串 + * @return 如果删除成功返回 true,否则返回 false + */ + override fun remove(key: String): Boolean { + return redisTemplate.delete(key) + } + + /** + * 判断指定键是否存在于缓存中。 + * + * @param key 缓存键,非空字符串 + * @return 如果存在返回 true,否则返回 false + */ + override fun exists(key: String): Boolean { + return redisTemplate.hasKey(key) + } + + /** + * 清空缓存中的所有数据,基于给定的命名空间和连接符进行模式匹配。 + * + * @param namespace 命名空间,用于限定要清除的键范围 + */ + override fun clear(namespace: String) { + require(namespace.isNotBlank()) { "namespace must not be blank" } + // 构造匹配模式 + val pattern = "$namespace${keyGenerator.getConnectors()}*" + while (true) { + // 扫描当前模式下的所有键 + val keys = redisKeyScanner.scan(pattern) + if (keys.isEmpty()) break + // 批量删除扫描到的键 + redisTemplate.delete(keys) + } + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/generator/DefaultKeyGenerator.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/generator/DefaultKeyGenerator.kt new file mode 100644 index 0000000..5568aa4 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/generator/DefaultKeyGenerator.kt @@ -0,0 +1,39 @@ +package com.gewuyou.forgeboot.cache.impl.generator + +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator + +/** + * 默认缓存键生成器 + * + * 该类负责生成带有命名空间的缓存键,通过将命名空间和键值拼接为 `namespace:key` 的形式。 + * + * @since 2025-06-18 12:40:04 + * @author gewuyou + */ +class DefaultKeyGenerator : KeyGenerator { + + /** + * 生成缓存键 + * + * 将给定的命名空间和键拼接成一个完整的缓存键,格式为 `namespace:key`。 + * + * @param namespace 命名空间,用于隔离不同的缓存区域 + * @param key 缓存项的具体键值 + * @return 拼接后的完整缓存键 + */ + override fun generateKey(namespace: String, key: String): String { + return "$namespace${this.getConnectors()}$key" + } + + /** + * 获取连接符 + * + * 返回用于拼接命名空间和基础键值的连接符,默认实现可能返回冒号(:)或其他特定字符。 + * 此方法允许子类自定义连接符,以满足不同存储结构或命名规范的需求。 + * + * @return 返回连接符字符串 + */ + override fun getConnectors(): String { + return ":" + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/manager/DefaultCacheManager.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/manager/DefaultCacheManager.kt new file mode 100644 index 0000000..292d1e6 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/manager/DefaultCacheManager.kt @@ -0,0 +1,74 @@ +package com.gewuyou.forgeboot.cache.impl.manager + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.api.manager.CacheManager +import com.gewuyou.forgeboot.cache.api.service.CacheService +import com.gewuyou.forgeboot.cache.impl.service.DefaultCacheService +import com.gewuyou.forgeboot.core.serialization.serializer.ValueSerializer +import java.util.concurrent.ConcurrentHashMap + +/** + * 默认缓存管理器 + * + * 该类负责管理不同命名空间下的缓存服务实例,通过懒加载方式创建 CacheService, + * 并提供统一的清除缓存接口。 + * + * @since 2025-06-18 13:39:07 + * @author gewuyou + */ +class DefaultCacheManager( + private val cacheProperties: CacheProperties, + private val cache: Cache, + private val serializer: ValueSerializer, + private val keyGenerator: KeyGenerator +) : CacheManager { + + /** + * 缓存服务实例的线程安全映射表,键为命名空间,值为对应的 CacheService 实例。 + * 使用 ConcurrentHashMap 确保多线程环境下的安全访问。 + */ + private val serviceMap = ConcurrentHashMap() + + /** + * 获取指定命名空间的缓存服务。 + * + * 如果该命名空间尚未存在,则使用提供的参数创建一个新的 DefaultCacheService 实例; + * 否则返回已存在的实例。 + * + * @param namespace 缓存命名空间标识符 + * @return 对应命名空间的缓存服务实例 + */ + override fun getCache(namespace: String): CacheService { + return serviceMap.computeIfAbsent(namespace) { + DefaultCacheService( + serializer = serializer, + cache = cache, + namespace = namespace, + cacheProperties = cacheProperties, + keyGenerator = keyGenerator + ) + } + } + + /** + * 清除指定命名空间的缓存内容。 + * + * 如果该命名空间存在对应的缓存服务,则调用其 clear 方法进行清除。 + * + * @param namespace 要清除缓存的命名空间标识符 + */ + override fun clear(namespace: String) { + serviceMap[namespace]?.clear(namespace) + } + + /** + * 清除所有命名空间的缓存内容。 + * + * 遍历 serviceMap 中的所有缓存服务,并分别调用它们的 clear 方法。 + */ + override fun clearAll() { + serviceMap.forEach { (ns, svc) -> svc.clear(ns) } + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/AllowNullCachePolicy.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/AllowNullCachePolicy.kt new file mode 100644 index 0000000..2716d3d --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/AllowNullCachePolicy.kt @@ -0,0 +1,49 @@ +package com.gewuyou.forgeboot.cache.impl.policy + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.api.policy.NullValuePolicy + +/** + * 允许 Null 缓存策略 + * + * 该策略允许缓存 null 值,并提供相应的占位符机制。 + * + * @since 2025-06-20 21:42:24 + * @author gewuyou + */ +class AllowNullCachePolicy( + private val cacheProperties: CacheProperties +) : NullValuePolicy { + + /** + * 判断是否允许缓存 null 值。 + * + * @param key 缓存项的键 + * @return Boolean 表示是否允许缓存 null 值,始终返回 true + */ + override fun allowCacheNull(key: String): Boolean { + // 始终允许缓存 null 值 + return true + } + + /** + * 获取表示 null 值的占位符。 + * + * @return String 表示 null 值的占位符 + */ + override fun nullPlaceholder(): String { + // 定义一个占位符,表示 null 值 + return cacheProperties.nullValuePlaceholder + } + + /** + * 判断给定值是否为 null 占位符。 + * + * @param value 要检查的值 + * @return Boolean 表示给定值是否为 null 占位符 + */ + override fun isNullPlaceholder(value: String): Boolean { + // 判断值是否为 null 占位符 + return value == cacheProperties.nullValuePlaceholder + } +} diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/DisallowNullCachePolicy.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/DisallowNullCachePolicy.kt new file mode 100644 index 0000000..64d9aa2 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/DisallowNullCachePolicy.kt @@ -0,0 +1,52 @@ +package com.gewuyou.forgeboot.cache.impl.policy + +import com.gewuyou.forgeboot.cache.api.policy.NullValuePolicy + +/** + * 禁止 Null 缓存策略 + * + * 该策略实现不允许缓存 null 值的行为,所有与 null 相关的操作都会被禁止并抛出异常。 + * + * @since 2025-06-20 21:43:00 + * @author gewuyou + */ +class DisallowNullCachePolicy : NullValuePolicy { + + /** + * 判断是否允许缓存 null 值。 + * + * @param key 缓存键,用于标识缓存项 + * @return Boolean 返回 false 表示始终不允许缓存 null 值 + */ + override fun allowCacheNull(key: String): Boolean { + // 始终不允许缓存 null 值 + return false + } + + /** + * 获取 null 值的占位符。 + * + * 由于不允许缓存 null 值,调用此方法会抛出 UnsupportedOperationException。 + * + * @return String 不会返回任何值,总是抛出异常 + * @throws UnsupportedOperationException 因为不允许缓存 null 值 + */ + override fun nullPlaceholder(): String { + // 该策略不会使用占位符,因为不允许缓存 null + throw UnsupportedOperationException("Cannot use null placeholder when null is not allowed in cache.") + } + + /** + * 判断指定值是否是 null 占位符。 + * + * 由于不允许缓存 null 值,此方法无法执行有效判断并会抛出异常。 + * + * @param value 要检查的值 + * @return Boolean 不会返回任何值,总是抛出异常 + * @throws UnsupportedOperationException 因为不允许缓存 null 值 + */ + override fun isNullPlaceholder(value: String): Boolean { + // 无法判断 null 占位符,因为 null 值不被缓存 + throw UnsupportedOperationException("Cannot check null placeholder when null is not allowed in cache.") + } +} diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/NullValueCachePolicy.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/NullValueCachePolicy.kt new file mode 100644 index 0000000..d302654 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/policy/NullValueCachePolicy.kt @@ -0,0 +1,49 @@ +package com.gewuyou.forgeboot.cache.impl.policy + +import com.gewuyou.forgeboot.cache.api.policy.CachePolicy +import com.gewuyou.forgeboot.cache.api.policy.NullValuePolicy + +/** + * 空值缓存策略 + * 用于处理缓存中空值(null)的存储和占位逻辑 + * + * @property nullValuePolicy 提供空值处理策略的接口实现 + * @constructor 创建一个 NullValueCachePolicy 实例 + * @param nullValuePolicy 指定的空值处理策略对象,不可为 null + * @since 2025-06-21 11:56:54 + * @author gewuyou + */ +class NullValueCachePolicy( + private val nullValuePolicy: NullValuePolicy +) : CachePolicy { + + /** + * 应用当前策略对指定的缓存值进行处理 + * + * @param key 缓存键,用于判断是否允许缓存空值 + * @param value 需要处理的缓存值,可能为 null + * @return 处理后的缓存值: + * - 如果值为 null 且允许缓存,则返回 null 占位符 + * - 如果值为 null 但不允许缓存,则返回 null + * - 否则返回原始非 null 值 + */ + override fun apply(key: String, value: String?): String? { + return when { + // 当值为空且策略允许缓存空值时,返回占位符 + value == null && nullValuePolicy.allowCacheNull(key) -> nullValuePolicy.nullPlaceholder() + // 当值为空但策略不允许缓存时,直接返回 null + value == null -> null + // 非空值直接返回 + else -> value + } + } + + /** + * 获取该策略的优先级,用于决定多个策略的执行顺序 + * + * @return 优先级,值越小表示优先级越高 + */ + override fun getOrder(): Int { + return 1 + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/runner/CacheWarmUpRunner.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/runner/CacheWarmUpRunner.kt new file mode 100644 index 0000000..54b35bf --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/runner/CacheWarmUpRunner.kt @@ -0,0 +1,32 @@ +package com.gewuyou.forgeboot.cache.impl.runner + +import com.gewuyou.forgeboot.cache.api.service.CacheWarmUpService +import org.springframework.boot.ApplicationArguments +import org.springframework.boot.ApplicationRunner + +/** + * 缓存预热运行程序 + * + * 该类实现 ApplicationRunner 接口,用于在应用启动完成后执行缓存预热操作。 + * 构造函数注入了多个 CacheWarmUpService 实现,每个服务对应一个需要预热的缓存资源。 + * + * @property services 需要执行预热操作的所有缓存服务集合 + * + * @since 2025-06-18 12:36:40 + * @author gewuyou + */ +class CacheWarmUpRunner( + private val services: List +) : ApplicationRunner { + /** + * 应用启动完成后执行的方法 + * + * 遍历所有注入的 CacheWarmUpService 并调用 warmUp 方法执行缓存预热逻辑。 + * 该方法会在 Spring Boot 应用完成启动后自动触发。 + * + * @param args 应用启动参数(可为空) + */ + override fun run(args: ApplicationArguments) { + services.forEach { it.warmUp() } + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/service/DefaultCacheService.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/service/DefaultCacheService.kt new file mode 100644 index 0000000..9b595d0 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/service/DefaultCacheService.kt @@ -0,0 +1,93 @@ +package com.gewuyou.forgeboot.cache.impl.service + +import com.gewuyou.forgeboot.cache.api.config.CacheProperties +import com.gewuyou.forgeboot.cache.api.contract.Cache +import com.gewuyou.forgeboot.cache.api.extension.get +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.api.service.CacheService +import com.gewuyou.forgeboot.core.serialization.serializer.ValueSerializer +import java.time.Duration + + +/** + * 默认缓存服务实现类,提供基于前缀的键值对缓存操作。 + * + * 该类实现了通用的缓存功能,包括获取、设置、删除、判断存在性和清空缓存的方法。 + * 所有方法均通过命名空间隔离不同业务的数据,确保缓存管理的安全性和灵活性。 + * + * @property serializer 序列化/反序列化缓存值的工具 + * @property cache 实际存储数据的复合缓存实例 + * @property namespace 命名空间 + * @property cacheProperties 缓存配置属性,包含默认过期时间等 + * @since 2025-06-16 22:56:49 + * @author gewuyou + */ +class DefaultCacheService( + private val serializer: ValueSerializer, + private val cache: Cache, + private val namespace: String, + private val cacheProperties: CacheProperties, + private val keyGenerator: KeyGenerator, +) : CacheService { + + /** + * 获取指定键的缓存值。 + * + * 在命名空间下拼接 key,并从缓存中获取对应的值。若值存在,则使用序列化器将其转换为指定类型;否则返回 null。 + * + * @param key 缓存键 + * @param type 值的类型 Class + * @return 缓存中的对象,如果不存在则为 null + */ + override fun retrieve(key: String, type: Class): T? { + return cache[keyGenerator.generateKey(namespace,key)]?.let { serializer.deserialize(it, type) } + } + + /** + * 设置缓存值。 + * + * 将值序列化后,在命名空间下拼接 key 并写入缓存。支持自定义 TTL(生存时间),若未指定则使用默认 TTL。 + * + * @param key 缓存键 + * @param value 值(支持任意对象) + * @param ttl 缓存时长,null 表示使用默认 TTL + */ + override fun put(key: String, value: Any, ttl: Duration?) { + cache.put(keyGenerator.generateKey(namespace,key), serializer.serialize(value), (ttl ?: cacheProperties.theDefaultCacheTTL)) + } + + /** + * 删除缓存。 + * + * 在命名空间下拼接 key,并尝试从缓存中移除对应条目。 + * + * @param key 缓存键 + * @return 删除成功返回 true + */ + override fun remove(key: String): Boolean { + return cache.remove(keyGenerator.generateKey(namespace,key)) + } + + /** + * 判断缓存是否存在。 + * + * 检查在命名空间下拼接的 key 是否存在于缓存中。 + * + * @param key 缓存键 + * @return 如果存在返回 true,否则返回 false + */ + override fun exists(key: String): Boolean { + return cache.exists(keyGenerator.generateKey(namespace,key)) + } + + /** + * 清空指定命名空间下的所有缓存数据。 + * + * 直接调用底层缓存实现的 clear 方法,传入命名空间参数。 + * + * @param namespace 命名空间 + */ + override fun clear(namespace: String) { + cache.clear(namespace) + } +} diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/service/RedisLockService.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/service/RedisLockService.kt new file mode 100644 index 0000000..a10ab5d --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/service/RedisLockService.kt @@ -0,0 +1,106 @@ +package com.gewuyou.forgeboot.cache.impl.service + +import com.gewuyou.forgeboot.cache.api.exception.CacheException +import com.gewuyou.forgeboot.cache.api.service.LockService +import org.redisson.api.RLock +import org.redisson.api.RedissonClient +import java.time.Duration +import java.util.concurrent.TimeUnit + +/** + * Redis 锁服务实现类 + * + * 该类通过 Redisson 提供分布式锁功能,确保在分布式环境下关键代码块的线程安全执行。 + * + * @property redissonClient Redisson 客户端实例,用于获取和操作分布式锁 + * @since 2025-06-18 21:17:21 + * @author gewuyou + */ +class RedisLockService( + private val redissonClient: RedissonClient +) : LockService { + + /** + * 在分布式锁保护下执行指定的操作 + * + * 尝试获取指定 key 的分布式锁,若成功则执行 supplier 函数并确保最终释放锁; + * 若未能在指定时间内获取锁,则抛出 CacheException 异常。 + * + * @param key 分布式锁的唯一标识 + * @param timeout 获取锁的最大等待时间 + * @param supplier 需要在锁保护下执行的业务逻辑 + * @return supplier 执行后的返回结果 + * @throws CacheException 如果无法获取到分布式锁 + */ + override fun executeWithLock(key: String, timeout: Duration, supplier: () -> T): T { + // 获取 Redisson 分布式锁实例 + val lock: RLock = redissonClient.getLock(key) + // 尝试在指定时间内获取锁 + val acquired: Boolean = lock.tryLock(timeout.seconds, TimeUnit.SECONDS) + + if (!acquired) { + throw CacheException("failed to acquire distributed lock:key=$key") + } + return try { + // 执行受锁保护的业务逻辑 + supplier() + } finally { + // 确保当前线程持有锁的情况下释放锁 + if (lock.isHeldByCurrentThread) { + lock.unlock() + } + } + } + + /** + * 使用读锁在受保护的上下文中执行指定操作 + * + * 获取指定 key 的读锁并在限定时间内执行 supplier。如果无法获得锁则抛出异常。 + * + * @param key 读锁的唯一标识 + * @param timeout 获取锁的最大等待时间 + * @param supplier 需要在读锁保护下执行的业务逻辑 + * @return supplier 执行后的返回结果 + * @throws CacheException 如果无法获取到读锁 + */ + override fun executeWithReadLock(key: String, timeout: Duration, supplier: () -> T): T { + val rwLock = redissonClient.getReadWriteLock(key).readLock() + val acquired = rwLock.tryLock(timeout.seconds, TimeUnit.SECONDS) + if (!acquired) { + throw CacheException("failed to obtain a read lock:$key") + } + return try { + supplier() + } finally { + if (rwLock.isHeldByCurrentThread) { + rwLock.unlock() + } + } + } + + /** + * 使用写锁在受保护的上下文中执行指定操作 + * + * 获取指定 key 的写锁并在限定时间内执行 supplier。如果无法获得锁则抛出异常。 + * + * @param key 写锁的唯一标识 + * @param timeout 获取锁的最大等待时间 + * @param supplier 需要在写锁保护下执行的业务逻辑 + * @return supplier 执行后的返回结果 + * @throws CacheException 如果无法获取到写锁 + */ + override fun executeWithWriteLock(key: String, timeout: Duration, supplier: () -> T): T { + val rwLock = redissonClient.getReadWriteLock(key).writeLock() + val acquired = rwLock.tryLock(timeout.seconds, TimeUnit.SECONDS) + if (!acquired) { + throw CacheException("failed to obtain a write lock:$key") + } + return try { + supplier() + } finally { + if (rwLock.isHeldByCurrentThread) { + rwLock.unlock() + } + } + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/support/CacheSpELHelper.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/support/CacheSpELHelper.kt new file mode 100644 index 0000000..6f5a791 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/support/CacheSpELHelper.kt @@ -0,0 +1,51 @@ +package com.gewuyou.forgeboot.cache.impl.support + +import com.gewuyou.forgeboot.cache.api.generator.KeyGenerator +import com.gewuyou.forgeboot.cache.impl.utils.SpELResolver +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.reflect.MethodSignature + +/** + * 缓存 SpEL 表达式帮助类 + * 提供基于方法参数的 SpEL 解析和缓存键生成能力 + * + * @since 2025-06-18 21:04:03 + * @author gewuyou + */ +object CacheSpELHelper { + + /** + * 构建方法参数名称到参数值的映射关系 + * 用于支持 SpEL 表达式解析时的参数上下文 + * + * @param joinPoint AOP 方法连接点,包含方法签名和参数信息 + * @return 参数名到参数值的映射表 + */ + fun buildArgsMap(joinPoint: JoinPoint): Map { + val method = (joinPoint.signature as MethodSignature).method + // 将方法参数列表转换为(参数名 -> 实际参数值)的键值对集合 + return method.parameters.mapIndexed { i, param -> param.name to joinPoint.args[i] }.toMap() + } + + /** + * 解析 SpEL 表达式并生成最终缓存键 + * + * @param namespace 缓存命名空间,用于隔离不同业务的缓存键 + * @param keySpEL SpEL 表达式字符串,定义缓存键的动态生成逻辑 + * @param joinPoint AOP 方法连接点,提供运行时参数上下文 + * @param keyGenerator 用户定义的缓存键生成策略 + * @return 最终生成的缓存键字符串 + */ + fun parseKey( + namespace: String, + keySpEL: String, + joinPoint: JoinPoint, + keyGenerator: KeyGenerator + ): String { + val argsMap = buildArgsMap(joinPoint) + // 使用 SpEL 解析器根据参数上下文解析表达式得到原始键值 + val key = SpELResolver.parse(keySpEL, argsMap) + // 委托给键生成器结合命名空间生成最终缓存键 + return keyGenerator.generateKey(namespace, key) + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/utils/RedisKeyScanner.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/utils/RedisKeyScanner.kt new file mode 100644 index 0000000..650a0bb --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/utils/RedisKeyScanner.kt @@ -0,0 +1,38 @@ +package com.gewuyou.forgeboot.cache.impl.utils + +import org.springframework.data.redis.connection.RedisConnectionFactory +import org.springframework.data.redis.core.ScanOptions +import java.nio.charset.StandardCharsets + +/** + *Redis 键扫描器 + * + * @since 2025-06-17 21:32:15 + * @author gewuyou + */ +class RedisKeyScanner( + private val redisConnectionFactory: RedisConnectionFactory +) { + + /** + * 扫描指定 pattern 下的所有 key,使用非阻塞 SCAN 命令 + * + * @param pattern 通配符,如 "user:*" + * @param count 每次扫描的数量 默认1000 + * @return 匹配到的所有 key 列表 + */ + fun scan(pattern: String, count: Long = 1000): Set { + require(pattern.isNotBlank()) { "Scan pattern must not be blank" } + val keys = mutableSetOf() + redisConnectionFactory.connection.use { connection -> + val options = ScanOptions.scanOptions().match(pattern).count(count).build() + connection.keyCommands().scan(options).use { cursor -> + while (cursor.hasNext()) { + val rawKey = cursor.next() + keys.add(String(rawKey, StandardCharsets.UTF_8)) + } + } + } + return keys + } +} \ No newline at end of file diff --git a/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/utils/SpELResolver.kt b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/utils/SpELResolver.kt new file mode 100644 index 0000000..af895c3 --- /dev/null +++ b/forgeboot-cache/forgeboot-cache-impl/src/main/kotlin/com/gewuyou/forgeboot/cache/impl/utils/SpELResolver.kt @@ -0,0 +1,35 @@ +package com.gewuyou.forgeboot.cache.impl.utils + +import org.springframework.expression.ExpressionParser +import org.springframework.expression.spel.standard.SpelExpressionParser +import org.springframework.expression.spel.support.StandardEvaluationContext + +/** + * SpEL 解析器,用于解析和执行 Spring Expression Language (SpEL) 表达式。 + * + * @since 2025-06-18 20:44:10 + * @author gewuyou + */ +object SpELResolver { + /** + * 表达式解析器实例,用于解析 SpEL 表达式。 + */ + private val parser: ExpressionParser = SpelExpressionParser() + + /** + * 解析给定的 SpEL 表达式,并使用提供的参数进行求值。 + * + * @param expression SpEL 表达式字符串 + * @param argsMap 包含变量及其对应值的映射表,用于表达式求值 + * @return 解析后的字符串结果,如果表达式返回 null,则返回空字符串 + */ + fun parse(expression: String, argsMap: Map): String { + // 创建评估上下文并设置变量 + val context = StandardEvaluationContext().apply { + argsMap.forEach { (name, value) -> setVariable(name, value) } + } + + // 解析并获取表达式的字符串值 + return parser.parseExpression(expression).getValue(context, String::class.java) ?: "" + } +} \ No newline at end of file diff --git a/forgeboot-security/.gitattributes b/forgeboot-security/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/.gitignore b/forgeboot-security/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-security/forgeboot-security-authenticate/.gitattributes b/forgeboot-security/forgeboot-security-authenticate/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-authenticate/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-authenticate/.gitignore b/forgeboot-security/forgeboot-security-authenticate/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/forgeboot-security-authenticate/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-security/forgeboot-security-authenticate/api/.gitattributes b/forgeboot-security/forgeboot-security-authenticate/api/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-authenticate/api/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-authenticate/api/.gitignore b/forgeboot-security/forgeboot-security-authenticate/api/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/forgeboot-security-authenticate/api/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-security/forgeboot-security-authenticate/autoconfigure/.gitattributes b/forgeboot-security/forgeboot-security-authenticate/autoconfigure/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-authenticate/autoconfigure/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-authenticate/autoconfigure/.gitignore b/forgeboot-security/forgeboot-security-authenticate/autoconfigure/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/forgeboot-security-authenticate/autoconfigure/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-security/forgeboot-security-authenticate/impl/.gitattributes b/forgeboot-security/forgeboot-security-authenticate/impl/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-authenticate/impl/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-authenticate/impl/.gitignore b/forgeboot-security/forgeboot-security-authenticate/impl/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/forgeboot-security-authenticate/impl/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-security/forgeboot-security-authorize/.gitattributes b/forgeboot-security/forgeboot-security-authorize/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-authorize/.gitignore b/forgeboot-security/forgeboot-security-authorize/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-security/forgeboot-security-authorize/api/.gitattributes b/forgeboot-security/forgeboot-security-authorize/api/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/api/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-authorize/api/.gitignore b/forgeboot-security/forgeboot-security-authorize/api/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/api/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/forgeboot-security/forgeboot-security-authorize/autoconfigure/.gitattributes b/forgeboot-security/forgeboot-security-authorize/autoconfigure/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/autoconfigure/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-authorize/autoconfigure/.gitignore b/forgeboot-security/forgeboot-security-authorize/autoconfigure/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/autoconfigure/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ForgeSecurityAuthorizeCoreConfiguration.kt b/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ForgeSecurityAuthorizeCoreConfiguration.kt new file mode 100644 index 0000000..63c712b --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ForgeSecurityAuthorizeCoreConfiguration.kt @@ -0,0 +1,77 @@ +package com.gewuyou.forgeboot.security.authorize.autoconfigure + +import com.gewuyou.forgeboot.security.authorize.api.manager.AccessManager +import com.gewuyou.forgeboot.security.authorize.api.provider.PermissionProvider +import com.gewuyou.forgeboot.security.authorize.api.resolver.PermissionResolver +import com.gewuyou.forgeboot.security.authorize.api.strategy.AuthorizationStrategy +import com.gewuyou.forgeboot.security.authorize.impl.manager.DefaultAccessManager +import com.gewuyou.forgeboot.security.authorize.impl.resolver.DefaultPermissionResolver +import com.gewuyou.forgeboot.security.authorize.impl.strategy.AnyMatchStrategy +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * Forge Security Authorize 核心配置 + * + * 该配置类定义了权限控制所需的核心组件Bean,包括权限解析器、授权策略和访问管理器。 + * 这些Bean用于实现基于权限表达式的访问控制机制。 + * + * @since 2025-06-24 16:49:37 + * @author gewuyou + */ +@Configuration +class ForgeSecurityAuthorizeCoreConfiguration { + /** + * 权限解析器Bean,用于将权限表达式解析为具体的权限对象。 + * + * 权限解析器是访问控制的基础组件,负责解析配置中的权限表达式, + * 将其转换为系统可识别和处理的权限对象。 + * + * @return 返回一个具体的权限解析器实例,此处为DefaultPermissionResolver。 + * 该实例实现了基本的权限解析功能。 + */ + @Bean + @ConditionalOnBean + fun permissionResolver(): PermissionResolver { + return DefaultPermissionResolver() + } + + /** + * 授权策略Bean,用于定义权限匹配的策略。 + * + * 授权策略决定了如何根据用户权限与资源所需权限进行匹配, + * 是决定访问是否被允许的关键逻辑所在。 + * + * @return 返回一个具体授权策略实例,此处为AnyMatchStrategy(任意匹配策略)。 + * 该策略表示只要存在任意一个匹配的权限即可允许访问。 + */ + @Bean + @ConditionalOnBean + fun authorizationStrategy(): AuthorizationStrategy { + return AnyMatchStrategy() + } + + /** + * 访问管理器Bean,负责处理访问控制决策。 + * + * 访问管理器是整个权限控制的核心组件,它整合了权限提供者和授权策略, + * 在访问请求到来时,使用权限提供者获取权限信息,并通过策略进行决策。 + * + * @param providers 权限提供者列表,用于获取不同资源所需的权限信息。 + * 每个Provider通常对应一种资源类型或数据来源。 + * @param strategy 授权策略实例,用于决定是否允许访问。 + * 该策略将影响最终访问控制决策。 + * + * @return 返回一个AccessManager的具体实现,此处为DefaultAccessManager。 + * 该实现组合了权限提供者和授权策略,完成了完整的访问控制流程。 + */ + @Bean + @ConditionalOnBean + fun accessManager( + providers: List, + strategy: AuthorizationStrategy, + ): AccessManager { + return DefaultAccessManager(providers, strategy) + } +} \ No newline at end of file diff --git a/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ReactiveAuthorizeSecurityConfiguration.kt b/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ReactiveAuthorizeSecurityConfiguration.kt new file mode 100644 index 0000000..c5ce3e5 --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ReactiveAuthorizeSecurityConfiguration.kt @@ -0,0 +1,118 @@ +package com.gewuyou.forgeboot.security.authorize.autoconfigure + +import com.fasterxml.jackson.databind.ObjectMapper +import com.gewuyou.forgeboot.security.authorize.api.config.SecurityAuthorizeProperties +import com.gewuyou.forgeboot.security.authorize.api.manager.AccessManager +import com.gewuyou.forgeboot.security.authorize.api.resolver.PermissionResolver +import com.gewuyou.forgeboot.security.authorize.impl.adapter.DynamicReactiveAuthorizationManagerAdapter +import com.gewuyou.forgeboot.security.authorize.impl.builder.StatelessSecurityWebFilterChainRegistrar +import com.gewuyou.forgeboot.security.authorize.impl.handler.ReactiveAuthorizationExceptionHandler +import com.gewuyou.forgeboot.security.core.common.builder.SecurityWebFilterChainRegistrar +import com.gewuyou.forgeboot.security.core.common.customizer.ServerHttpSecurityCustomizer +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.authorization.ReactiveAuthorizationManager +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity +import org.springframework.security.config.web.server.ServerHttpSecurity +import org.springframework.security.web.server.SecurityWebFilterChain +import org.springframework.security.web.server.authorization.AuthorizationContext +import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.anyExchange + +/** + * 反应式授权配置 + * 配置响应式安全过滤链和相关组件,用于处理WebFlux环境下的安全控制。 + * + * @since 2025-06-24 16:48:11 + * @author gewuyou + */ +@Configuration(proxyBeanMethods = false) +@EnableWebFluxSecurity +@ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "reactive") +class ReactiveAuthorizeSecurityConfiguration( + private val accessManager: AccessManager, + private val permissionResolver: PermissionResolver, +) { + + /** + * 创建默认的安全过滤链,适用于响应式编程模型 + * 使用给定的注册器和配置构建一个默认的安全过滤链,该链拒绝所有请求。 + * + * @param registrar 安全过滤链注册器,用于注册新的安全过滤链 + * @param http ServerHttpSecurity配置对象,用于定义安全规则 + * @return 构建后的安全过滤链实例 + * @throws Exception 构建过程中可能抛出的异常 + */ + @Bean(name = ["defaultSecurityWebFilterChain"]) + @ConditionalOnBean + @Throws(Exception::class) + fun defaultSecurityWebFilterChain( + registrar: SecurityWebFilterChainRegistrar, + http: ServerHttpSecurity, + ): SecurityWebFilterChain = registrar + .buildChain( + "default", + http, + anyExchange() + ) { it: ServerHttpSecurity -> + it.authorizeExchange { + it.anyExchange().denyAll() + } + } + + /** + * 注册响应式授权异常处理器 + * 创建并返回一个用于处理授权失败情况的异常处理器。 + * + * @param objectMapper 用于序列化错误响应的对象映射器 + * @param securityAuthorizeProperties 授权配置属性,包含自定义的授权设置 + * @return ServerAccessDeniedHandler 实例,用于处理访问被拒绝的情况 + */ + @Bean + @ConditionalOnClass(ServerAccessDeniedHandler::class) + @ConditionalOnBean + fun reactiveAuthorizationExceptionHandler( + objectMapper: ObjectMapper, + securityAuthorizeProperties: SecurityAuthorizeProperties, + ): ServerAccessDeniedHandler { + return ReactiveAuthorizationExceptionHandler(objectMapper, securityAuthorizeProperties) + } + + /** + * 动态响应式授权管理器适配器 + * 创建并返回一个适配后的响应式授权管理器实例,用于动态评估访问请求。 + * + * @return 适配后的响应式授权管理器实例 + */ + @Bean + @ConditionalOnBean + fun dynamicReactiveAuthorizationManager(): ReactiveAuthorizationManager { + return DynamicReactiveAuthorizationManagerAdapter(accessManager, permissionResolver) + } + + /** + * 无状态安全过滤链注册器 + * 创建并返回一个无状态的安全过滤链注册器,用于注册需要无状态处理的安全过滤链。 + * + * @param reactiveAuthorizationManager 响应式授权管理器,用于评估访问请求 + * @param reactiveAccessDeniedHandler 授权失败处理器,用于处理访问被拒绝的情况 + * @param serverHttpSecurityCustomizer 安全配置定制器列表,用于自定义安全配置 + * @return 安全过滤链注册器实例,用于注册无状态安全过滤链 + */ + @Bean + @ConditionalOnBean + fun statelessSecurityWebFilterChainRegistrar( + reactiveAuthorizationManager: ReactiveAuthorizationManager, + reactiveAccessDeniedHandler: ServerAccessDeniedHandler, + serverHttpSecurityCustomizer: List, + ): SecurityWebFilterChainRegistrar { + return StatelessSecurityWebFilterChainRegistrar( + serverHttpSecurityCustomizer, + reactiveAuthorizationManager, + reactiveAccessDeniedHandler + ) + } +} \ No newline at end of file diff --git a/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ServletAuthorizeSecurityConfiguration.kt b/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ServletAuthorizeSecurityConfiguration.kt new file mode 100644 index 0000000..00e1b88 --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/security/authorize/autoconfigure/ServletAuthorizeSecurityConfiguration.kt @@ -0,0 +1,109 @@ +package com.gewuyou.forgeboot.security.authorize.autoconfigure + +import com.fasterxml.jackson.databind.ObjectMapper +import com.gewuyou.forgeboot.security.authorize.api.config.SecurityAuthorizeProperties +import com.gewuyou.forgeboot.security.authorize.api.manager.AccessManager +import com.gewuyou.forgeboot.security.authorize.api.resolver.PermissionResolver +import com.gewuyou.forgeboot.security.authorize.impl.adapter.DynamicAuthorizationManagerAdapter +import com.gewuyou.forgeboot.security.authorize.impl.builder.StatelessSecurityFilterChainRegistrar +import com.gewuyou.forgeboot.security.authorize.impl.handler.AuthorizationExceptionHandler +import com.gewuyou.forgeboot.security.core.common.builder.SecurityFilterChainRegistrar +import com.gewuyou.forgeboot.security.core.common.customizer.HttpSecurityCustomizer +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.authorization.AuthorizationManager +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.web.SecurityFilterChain +import org.springframework.security.web.access.AccessDeniedHandler +import org.springframework.security.web.access.intercept.RequestAuthorizationContext +import org.springframework.security.web.util.matcher.AnyRequestMatcher + +/** + *servlet authorize 安全配置 + * + * @since 2025-06-24 16:54:19 + * @author gewuyou + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "servlet", matchIfMissing = true) +class ServletAuthorizeSecurityConfiguration( + private val accessManager: AccessManager, + private val permissionResolver: PermissionResolver, +) { + /** + * 创建默认的安全过滤链,适用于 Servlet 编程模型 + * + * @param registrar 安全过滤链注册器 + * @param http HttpSecurity 配置对象 + * @return 构建后的安全过滤链实例 + * @throws Exception 构建过程中可能抛出的异常 + */ + @Bean(name = ["defaultSecurityFilterChain"]) + @ConditionalOnBean(SecurityFilterChainRegistrar::class) + @Throws(Exception::class) + fun defaultSecurityFilterChain( + registrar: SecurityFilterChainRegistrar, + http: HttpSecurity, + ): SecurityFilterChain = registrar.buildChain( + "default", + http, + AnyRequestMatcher.INSTANCE + ) { config -> + config.authorizeHttpRequests { + it.anyRequest().denyAll() + } + } + + /** + * 注册授权异常处理器 + * + * @param objectMapper 用于序列化错误响应的对象映射器 + * @param securityAuthorizeProperties 授权配置属性 + * @return AccessDeniedHandler 实例 + */ + @Bean + @ConditionalOnClass(AccessDeniedHandler::class) + @ConditionalOnBean + fun authorizationExceptionHandler( + objectMapper: ObjectMapper, + securityAuthorizeProperties: SecurityAuthorizeProperties, + ): AccessDeniedHandler { + return AuthorizationExceptionHandler(objectMapper, securityAuthorizeProperties) + } + + /** + * 动态授权管理器适配器 + * + * @return 适配后的授权管理器实例 + */ + @Bean + @ConditionalOnBean + fun dynamicAuthorizationManager(): AuthorizationManager { + return DynamicAuthorizationManagerAdapter(accessManager, permissionResolver) + } + + /** + * 无状态安全过滤链注册器 + * + * @param authorizationManager 授权管理器 + * @param accessDeniedHandler 授权失败处理器 + * @param httpSecurityCustomizer HTTP 安全配置定制器列表 + * @return 安全过滤链注册器实例 + */ + @Bean + @ConditionalOnBean + fun statelessSecurityWebFilterChainRegistrar( + authorizationManager: AuthorizationManager, + accessDeniedHandler: AccessDeniedHandler, + httpSecurityCustomizer: List, + ): SecurityFilterChainRegistrar { + return StatelessSecurityFilterChainRegistrar( + httpSecurityCustomizer, + authorizationManager, + accessDeniedHandler + ) + } +} \ No newline at end of file diff --git a/forgeboot-security/forgeboot-security-authorize/impl/.gitattributes b/forgeboot-security/forgeboot-security-authorize/impl/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/impl/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-authorize/impl/.gitignore b/forgeboot-security/forgeboot-security-authorize/impl/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/forgeboot-security-authorize/impl/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-security/forgeboot-security-core/.gitattributes b/forgeboot-security/forgeboot-security-core/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-security/forgeboot-security-core/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-security/forgeboot-security-core/.gitignore b/forgeboot-security/forgeboot-security-core/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-security/forgeboot-security-core/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin