SpringBoot中向JsonDeserializer传递User实例的实现方案问询
实现带User参数的Jackson JsonDeserializer(Spring Boot 2.7.3)
因为Jackson的JsonDeserializer本身不支持直接传入外部参数(比如这里的User实例),结合你用Spring Boot 2.7.3且用@JsonComponent管理反序列化器的场景,可以用**线程局部变量(ThreadLocal)**来传递User实例,具体步骤如下:
1. 编写ThreadLocal工具类传递User
先搞个工具类存当前线程的User实例,确保反序列化过程中能拿到:
public class UserContextHolder { private static final ThreadLocal<User> USER_THREAD_LOCAL = new ThreadLocal<>(); public static void setUser(User user) { USER_THREAD_LOCAL.set(user); } public static User getUser() { return USER_THREAD_LOCAL.get(); } public static void clear() { USER_THREAD_LOCAL.remove(); } }
2. 实现带@JsonComponent的自定义反序列化器
用@JsonComponent注解让Spring自动把这个反序列化器注册到ObjectMapper里,同时可以正常@Autowired注入其他Bean:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import org.springframework.boot.jackson.JsonComponent; import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; @JsonComponent public class CustomConfigurationDeserializer extends JsonDeserializer<CustomConfiguration> { // 这里可以正常注入你需要的Spring Bean @Autowired private SomeService someService; @Override public CustomConfiguration deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 从ThreadLocal里拿到传入的User实例 User currentUser = UserContextHolder.getUser(); // 这里写你的反序列化逻辑,比如结合User和JSON数据生成CustomConfiguration CustomConfiguration config = p.readValueAs(CustomConfiguration.class); config.setCreatedBy(currentUser.getId()); // 示例:用User的ID设置配置的创建人 // 也可以调用注入的someService做业务处理 someService.processConfig(config, currentUser); return config; } }
3. 修改调用方法,传递User并清理ThreadLocal
在调用objectMapper.readValue之前,把User放到ThreadLocal里,并且用try-finally确保用完清理,避免内存泄漏:
import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class SomeConfigService { @Autowired private ObjectMapper objectMapper; public CustomConfiguration deserialize(User user, String data) { try { // 把User放到ThreadLocal UserContextHolder.setUser(user); // 这里调用readValue时,会自动触发上面的CustomConfigurationDeserializer return objectMapper.readValue(data, CustomConfiguration.class); } finally { // 必须清理ThreadLocal,防止线程复用导致的数据污染 UserContextHolder.clear(); } } }
注意事项
- 一定要用try-finally清理ThreadLocal,因为Spring的线程池会复用线程,不清理的话下次请求可能拿到之前的User实例。
- 如果你的User是Spring Security的认证用户,也可以直接从
SecurityContextHolder拿,不用自己写ThreadLocal工具类,不过这个场景下你需要手动传入User,所以还是用自定义ThreadLocal更合适。
内容的提问来源于stack exchange,提问作者Shvalb
相关产品推荐
相关产品推荐

