from claude

我来仔细分析这个方案,然后给你完整的代码实现。

对文档方案的评估

这个方案整体思路是正确的,但有几个细节需要优化:

✅ 正确的地方

  1. SPI抽象思路完全正确 - 通过JsonCodec接口隔离具体实现

  2. JsonType作为边界对象 - 确实能避免JSON库细节泄漏

  3. Kotlin reified支持 - 用户体验好

⚠️ 需要优化的地方

  1. JsonType的Type来源 - 文档中混用了typeOf().javaType和Gson的TypeToken,应该统一

  2. Java侧的TypeToken依赖 - 不应该让core依赖Gson,应该自己实现ParameterizedType

  3. 编码时的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.MoshiJsonCodec

3️⃣ 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.JacksonJsonCodec

4️⃣ 使用示例

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);
    }
}

总结

这个方案的核心优势:

  1. ✅ 真正的SPI隔离 - core模块零依赖任何JSON库

  2. ✅ Kotlin体验优秀 - reified支持,无需显式类型

  3. ✅ Java体验友好 - 清晰的工厂方法,不依赖Gson

  4. ✅ 可扩展 - 通过ServiceLoader或手动注入切换实现

  5. ✅ 类型安全 - 自己实现ParameterizedType,避免运行时类型擦除问题

  6. ✅ 未来可演进 - JsonType可以扩展携带更多元信息

这就是你需要的完整实现!🎯