from claude
我来仔细分析这个方案,然后给你完整的代码实现。
对文档方案的评估
这个方案整体思路是正确的,但有几个细节需要优化:
✅ 正确的地方
SPI抽象思路完全正确 - 通过
JsonCodec接口隔离具体实现JsonType作为边界对象 - 确实能避免JSON库细节泄漏
Kotlin reified支持 - 用户体验好
⚠️ 需要优化的地方
JsonType的Type来源 - 文档中混用了
typeOf().javaType和Gson的TypeToken,应该统一Java侧的TypeToken依赖 - 不应该让core依赖Gson,应该自己实现ParameterizedType
编码时的Type传递 -
encode时传入JsonType<T>有些冗余,可以优化
完整代码实现
1️⃣ Core模块 - 接口定义
// colleen-core/src/main/kotlin/JsonType.kt
package com.colleen.json
import java.lang.reflect.Type
/**
* 框架级类型令牌,用于在Kotlin和Java之间传递类型信息
* 这是一个边界对象,隔离了具体JSON库的类型系统
*/
abstract class JsonType<T> internal constructor(
internal val type: Type
) {
companion object {
/**
* 为Java提供的简单类型构造器
*/
@JvmStatic
fun <T> of(clazz: Class<T>): JsonType<T> =
object : JsonType<T>(clazz) {}
}
}
/**
* Kotlin侧:使用reified自动推导类型
*/
@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> jsonType(): JsonType<T> =
object : JsonType<T>(typeOf<T>().javaType) {}// colleen-core/src/main/kotlin/JsonTypes.kt
package com.colleen.json
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
/**
* 为Java用户提供的类型工厂
* 不依赖任何JSON库,自己实现ParameterizedType
*/
object JsonTypes {
@JvmStatic
fun <T> of(clazz: Class<T>): JsonType<T> =
JsonType.of(clazz)
@JvmStatic
fun <E> listOf(elementClass: Class<E>): JsonType<List<E>> =
object : JsonType<List<E>>(
ParameterizedTypeImpl(List::class.java, elementClass)
) {}
@JvmStatic
fun <K, V> mapOf(
keyClass: Class<K>,
valueClass: Class<V>
): JsonType<Map<K, V>> =
object : JsonType<Map<K, V>>(
ParameterizedTypeImpl(Map::class.java, keyClass, valueClass)
) {}
@JvmStatic
fun <T> setOf(elementClass: Class<T>): JsonType<Set<T>> =
object : JsonType<Set<T>>(
ParameterizedTypeImpl(Set::class.java, elementClass)
) {}
/**
* 自己实现的ParameterizedType,避免依赖Gson
*/
private class ParameterizedTypeImpl(
private val rawType: Type,
private vararg val typeArguments: Type
) : ParameterizedType {
override fun getRawType(): Type = rawType
override fun getActualTypeArguments(): Array<out Type> = typeArguments
override fun getOwnerType(): Type? = null
override fun equals(other: Any?): Boolean {
if (other !is ParameterizedType) return false
return rawType == other.rawType &&
typeArguments.contentEquals(other.actualTypeArguments)
}
override fun hashCode(): Int {
return rawType.hashCode() xor typeArguments.contentHashCode()
}
override fun toString(): String {
val args = typeArguments.joinToString(", ") { it.typeName }
return "${rawType.typeName}<$args>"
}
}
}// colleen-core/src/main/kotlin/JsonCodec.kt
package com.colleen.json
/**
* JSON序列化/反序列化的SPI接口
* 这是所有JSON库实现的统一契约
*/
interface JsonCodec {
/**
* 将字节数组反序列化为指定类型
* @param bytes JSON字节数组
* @param type 目标类型
* @return 反序列化后的对象
* @throws JsonDecodeException 反序列化失败时抛出
*/
fun <T> decode(bytes: ByteArray, type: JsonType<T>): T
/**
* 将对象序列化为字节数组
* @param value 要序列化的对象
* @return JSON字节数组
* @throws JsonEncodeException 序列化失败时抛出
*/
fun encode(value: Any?): ByteArray
}
/**
* JSON解码异常
*/
class JsonDecodeException(message: String, cause: Throwable? = null) :
RuntimeException(message, cause)
/**
* JSON编码异常
*/
class JsonEncodeException(message: String, cause: Throwable? = null) :
RuntimeException(message, cause)// colleen-core/src/main/kotlin/Request.kt
package com.colleen.core
import com.colleen.json.JsonCodec
import com.colleen.json.JsonType
import com.colleen.json.jsonType
/**
* HTTP请求对象
*/
class Request(
val method: String,
val path: String,
val headers: Map<String, String>,
val body: ByteArray?,
internal val jsonCodec: JsonCodec
) {
/**
* Kotlin侧: 使用reified自动推导类型
* 示例: val user = request.json<User>()
*/
inline fun <reified T> json(): T? {
val bytes = body ?: return null
return jsonCodec.decode(bytes, jsonType())
}
/**
* Java侧: 显式传入类型
* 示例: User user = request.json(User.class)
*/
fun <T> json(clazz: Class<T>): T? {
val bytes = body ?: return null
return jsonCodec.decode(bytes, JsonType.of(clazz))
}
/**
* Java侧: 支持泛型类型
* 示例: List<User> users = request.json(JsonTypes.listOf(User.class))
*/
fun <T> json(type: JsonType<T>): T? {
val bytes = body ?: return null
return jsonCodec.decode(bytes, type)
}
}// colleen-core/src/main/kotlin/Response.kt
package com.colleen.core
import com.colleen.json.JsonCodec
/**
* HTTP响应对象
*/
class Response(
internal val jsonCodec: JsonCodec
) {
private var statusCode: Int = 200
private var bodyBytes: ByteArray? = null
private val headers = mutableMapOf<String, String>()
fun status(code: Int): Response {
this.statusCode = code
return this
}
fun header(name: String, value: String): Response {
headers[name] = value
return this
}
/**
* 将对象序列化为JSON并设置响应体
* Kotlin: response.json(user)
* Java: response.json(user)
*/
fun json(value: Any?): Response {
bodyBytes = jsonCodec.encode(value)
headers["Content-Type"] = "application/json; charset=utf-8"
return this
}
fun getStatus(): Int = statusCode
fun getHeaders(): Map<String, String> = headers
fun getBody(): ByteArray? = bodyBytes
}2️⃣ Moshi实现模块
// colleen-json-moshi/build.gradle.kts
dependencies {
api(project(":colleen-core"))
implementation("com.squareup.moshi:moshi:1.15.0")
implementation("com.squareup.moshi:moshi-kotlin:1.15.0")
}// colleen-json-moshi/src/main/kotlin/MoshiJsonCodec.kt
package com.colleen.json.moshi
import com.colleen.json.*
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
/**
* Moshi实现的JsonCodec
*/
class MoshiJsonCodec(
private val moshi: Moshi = defaultMoshi()
) : JsonCodec {
override fun <T> decode(bytes: ByteArray, type: JsonType<T>): T {
return try {
val adapter = moshi.adapter<T>(type.type)
val json = bytes.decodeToString()
adapter.fromJson(json)
?: throw JsonDecodeException("JSON body is null or empty")
} catch (e: Exception) {
throw JsonDecodeException("Failed to decode JSON: ${e.message}", e)
}
}
override fun encode(value: Any?): ByteArray {
if (value == null) {
return "null".encodeToByteArray()
}
return try {
@Suppress("UNCHECKED_CAST")
val adapter = moshi.adapter(value::class.java) as com.squareup.moshi.JsonAdapter<Any>
adapter.toJson(value).encodeToByteArray()
} catch (e: Exception) {
throw JsonEncodeException("Failed to encode JSON: ${e.message}", e)
}
}
companion object {
/**
* 创建默认配置的Moshi实例
*/
fun defaultMoshi(): Moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()
}
}// colleen-json-moshi/src/main/resources/META-INF/services/com.colleen.json.JsonCodec
com.colleen.json.moshi.MoshiJsonCodec3️⃣ Jackson实现模块
// colleen-json-jackson/build.gradle.kts
dependencies {
api(project(":colleen-core"))
implementation("com.fasterxml.jackson.core:jackson-databind:2.16.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.16.0")
}// colleen-json-jackson/src/main/kotlin/JacksonJsonCodec.kt
package com.colleen.json.jackson
import com.colleen.json.*
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
/**
* Jackson实现的JsonCodec
*/
class JacksonJsonCodec(
private val mapper: ObjectMapper = defaultObjectMapper()
) : JsonCodec {
override fun <T> decode(bytes: ByteArray, type: JsonType<T>): T {
return try {
val javaType = mapper.typeFactory.constructType(type.type)
mapper.readValue(bytes, javaType)
} catch (e: Exception) {
throw JsonDecodeException("Failed to decode JSON: ${e.message}", e)
}
}
override fun encode(value: Any?): ByteArray {
return try {
mapper.writeValueAsBytes(value)
} catch (e: Exception) {
throw JsonEncodeException("Failed to encode JSON: ${e.message}", e)
}
}
companion object {
/**
* 创建默认配置的ObjectMapper
*/
fun defaultObjectMapper(): ObjectMapper = jacksonObjectMapper().apply {
// 忽略未知属性
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
// 不序列化null值
configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false)
// 日期格式
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
}
}
}// colleen-json-jackson/src/main/resources/META-INF/services/com.colleen.json.JsonCodec
com.colleen.json.jackson.JacksonJsonCodec4️⃣ 使用示例
Kotlin使用
import com.colleen.core.*
import com.colleen.json.moshi.MoshiJsonCodec
data class User(val name: String, val age: Int)
fun main() {
val jsonCodec = MoshiJsonCodec()
// 创建请求
val requestBody = """{"name":"Alice","age":25}""".encodeToByteArray()
val request = Request(
method = "POST",
path = "/users",
headers = emptyMap(),
body = requestBody,
jsonCodec = jsonCodec
)
// Kotlin侧: reified自动推导
val user = request.json<User>()
println(user) // User(name=Alice, age=25)
// 泛型支持
val listBody = """[{"name":"Bob","age":30}]""".encodeToByteArray()
val listRequest = Request("GET", "/", emptyMap(), listBody, jsonCodec)
val users = listRequest.json<List<User>>()
println(users) // [User(name=Bob, age=30)]
// 响应
val response = Response(jsonCodec)
response.json(user)
println(String(response.getBody()!!)) // {"name":"Alice","age":25}
}Java使用
import com.colleen.core.*;
import com.colleen.json.*;
import com.colleen.json.jackson.JacksonJsonCodec;
import java.util.List;
import java.util.Map;
public class Example {
public static void main(String[] args) {
JsonCodec jsonCodec = new JacksonJsonCodec();
// 简单对象
byte[] body = "{\"name\":\"Alice\",\"age\":25}".getBytes();
Request request = new Request("POST", "/users",
Map.of(), body, jsonCodec);
User user = request.json(User.class);
System.out.println(user);
// 泛型List
byte[] listBody = "[{\"name\":\"Bob\",\"age\":30}]".getBytes();
Request listRequest = new Request("GET", "/",
Map.of(), listBody, jsonCodec);
List<User> users = listRequest.json(JsonTypes.listOf(User.class));
System.out.println(users);
// 泛型Map
Map<String, User> userMap = request.json(
JsonTypes.mapOf(String.class, User.class)
);
// 响应
Response response = new Response(jsonCodec);
response.json(user);
}
}总结
这个方案的核心优势:
✅ 真正的SPI隔离 - core模块零依赖任何JSON库
✅ Kotlin体验优秀 - reified支持,无需显式类型
✅ Java体验友好 - 清晰的工厂方法,不依赖Gson
✅ 可扩展 - 通过ServiceLoader或手动注入切换实现
✅ 类型安全 - 自己实现ParameterizedType,避免运行时类型擦除问题
✅ 未来可演进 - JsonType可以扩展携带更多元信息
这就是你需要的完整实现!🎯