refactor(service): 修改IEntityService泛型为VO类型并优化缓存策略
重构所有注解@CacheConfig的Service类,将IEntityService泛型从实体类改为VO类 实现实体与VO之间的转换逻辑,使用VO替代实体进行缓存以避免序列化问题 更新相关依赖组件和测试用例,确保功能完整性和系统兼容性 优化Redis缓存配置,清理旧缓存数据并验证新缓存策略有效性
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package com.ecep.contract.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
/**
|
||||
* 自定义LocalDateTime反序列化器,支持多种格式
|
||||
*/
|
||||
class CustomLocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
|
||||
private final List<DateTimeFormatter> formatters;
|
||||
|
||||
public CustomLocalDateTimeDeserializer() {
|
||||
super(LocalDateTime.class);
|
||||
// 支持多种日期时间格式
|
||||
this.formatters = new ArrayList<>();
|
||||
// ISO标准格式
|
||||
this.formatters.add(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
|
||||
// 项目默认格式 yyyy-MM-dd HH:mm:ss
|
||||
this.formatters.add(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
// 其他可能的格式
|
||||
this.formatters.add(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));
|
||||
this.formatters.add(DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss"));
|
||||
this.formatters.add(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm"));
|
||||
this.formatters.add(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt)
|
||||
throws IOException, JsonProcessingException {
|
||||
String value = p.getText().trim();
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试使用各种格式解析
|
||||
for (DateTimeFormatter formatter : formatters) {
|
||||
try {
|
||||
return LocalDateTime.parse(value, formatter);
|
||||
} catch (Exception e) {
|
||||
// 尝试下一种格式
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有格式都失败,尝试使用ISO_INSTANT格式(可能是时间戳字符串)
|
||||
try {
|
||||
return LocalDateTime.ofInstant(Instant.parse(value), ZoneId.systemDefault());
|
||||
} catch (Exception e) {
|
||||
// 仍然失败,抛出异常
|
||||
throw new JsonParseException(p, "无法解析为LocalDateTime: " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.ecep.contract.config;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
|
||||
import com.ecep.contract.model.IdentityEntity;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
/**
|
||||
* 用于处理Hibernate代理对象的反序列化器
|
||||
* 专门处理序列化时写入的id和_proxy_字段
|
||||
*/
|
||||
class HibernateProxyDeserializer extends StdDeserializer<HibernateProxy> {
|
||||
|
||||
protected HibernateProxyDeserializer() {
|
||||
super(HibernateProxy.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HibernateProxy deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
// 检查是否是对象类型
|
||||
if (p.currentToken() != JsonToken.START_OBJECT) {
|
||||
return (HibernateProxy) ctxt.handleUnexpectedToken(HibernateProxy.class, p);
|
||||
}
|
||||
|
||||
// 读取对象字段
|
||||
JsonToken token;
|
||||
Object id = null;
|
||||
String proxyClassName = null;
|
||||
|
||||
// 解析JSON对象的所有字段
|
||||
while ((token = p.nextToken()) != JsonToken.END_OBJECT) {
|
||||
if (token == JsonToken.FIELD_NAME) {
|
||||
String fieldName = p.getCurrentName();
|
||||
p.nextToken(); // 移动到字段值
|
||||
|
||||
if ("id".equals(fieldName)) {
|
||||
id = p.readValueAs(Object.class);
|
||||
} else if ("_proxy_".equals(fieldName)) {
|
||||
proxyClassName = p.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果同时存在id和_proxy_字段,说明这是一个Hibernate代理对象
|
||||
if (id != null && proxyClassName != null) {
|
||||
|
||||
HibernateProxy proxy = null;
|
||||
|
||||
try {
|
||||
// 尝试获取实体类类型(去掉代理类名中的字节码增强部分)
|
||||
// 代理类名通常格式为:com.ecep.contract.model.Entity$HibernateProxy$XXXX
|
||||
int proxyIndex = proxyClassName.indexOf("$HibernateProxy$");
|
||||
String entityClassName = proxyIndex > 0 ? proxyClassName.substring(0, proxyIndex) : proxyClassName;
|
||||
|
||||
// 加载实体类
|
||||
Class<?> entityClass = Class.forName(entityClassName);
|
||||
|
||||
// 检查是否是IdentityEntity类型
|
||||
if (IdentityEntity.class.isAssignableFrom(entityClass)) {
|
||||
// 创建实体对象并设置ID
|
||||
IdentityEntity entity = (IdentityEntity) entityClass.getDeclaredConstructor().newInstance();
|
||||
if (id instanceof Number) {
|
||||
entity.setId(((Number) id).intValue());
|
||||
} else {
|
||||
// 处理非数值类型的ID
|
||||
try {
|
||||
entity.setId(Integer.parseInt(id.toString()));
|
||||
} catch (NumberFormatException e) {
|
||||
// 如果无法转换为Integer,记录警告但继续处理
|
||||
}
|
||||
}
|
||||
return (HibernateProxy) entity;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 发生异常时,返回一个只包含ID的简单对象
|
||||
// 这可以避免反序列化失败
|
||||
return (HibernateProxy) createSimpleIdObject(id);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果不是代理对象或处理失败,回退到默认的反序列化逻辑
|
||||
// 使用 skipChildren() 确保解析器在正确的位置开始默认反序列化
|
||||
if (p.currentToken() != JsonToken.START_OBJECT) {
|
||||
// 尝试查找对象开始位置
|
||||
while (p.currentToken() != null && p.currentToken() != JsonToken.START_OBJECT) {
|
||||
p.nextToken();
|
||||
}
|
||||
}
|
||||
if (p.currentToken() == JsonToken.START_OBJECT) {
|
||||
return (HibernateProxy) ctxt.readValue(p, Object.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个只包含ID字段的简单对象
|
||||
*/
|
||||
private Object createSimpleIdObject(final Object id) {
|
||||
return new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProxyObject[id=" + id + "]";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.ecep.contract.config;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
|
||||
import com.ecep.contract.model.IdentityEntity;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
/**
|
||||
* 专门用于处理HibernateProxy对象的序列化器
|
||||
*/
|
||||
class HibernateProxySerializer extends StdSerializer<HibernateProxy> {
|
||||
|
||||
protected HibernateProxySerializer() {
|
||||
super(HibernateProxy.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(HibernateProxy value, JsonGenerator gen, SerializerProvider serializers)
|
||||
throws IOException {
|
||||
// 尝试初始化代理对象,如果未初始化则只输出ID
|
||||
try {
|
||||
LazyInitializer lazyInitializer = value.getHibernateLazyInitializer();
|
||||
if (lazyInitializer.isUninitialized()) {
|
||||
// 如果代理对象未初始化,只输出ID
|
||||
if (lazyInitializer.getIdentifier() != null) {
|
||||
gen.writeStartObject();
|
||||
gen.writeFieldName("id");
|
||||
gen.writeObject(lazyInitializer.getIdentifier());
|
||||
gen.writeStringField("_proxy_", lazyInitializer.getEntityName());
|
||||
gen.writeEndObject();
|
||||
} else {
|
||||
gen.writeNull();
|
||||
}
|
||||
} else {
|
||||
// 如果代理对象已初始化,获取实际对象并序列化
|
||||
Object unwrapped = lazyInitializer.getImplementation();
|
||||
if (unwrapped instanceof IdentityEntity) {
|
||||
// 对于IdentityEntity类型,输出更简洁的格式
|
||||
gen.writeStartObject();
|
||||
gen.writeNumberField("id", ((IdentityEntity) unwrapped).getId());
|
||||
gen.writeEndObject();
|
||||
} else {
|
||||
// 对于非IdentityEntity类型,使用默认序列化
|
||||
serializers.defaultSerializeValue(unwrapped, gen);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果发生异常,输出最小化信息
|
||||
gen.writeStartObject();
|
||||
gen.writeStringField("error", "Failed to serialize Hibernate proxy: " + e.getMessage());
|
||||
gen.writeStringField("class", value.getClass().getName());
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeWithType(HibernateProxy value, JsonGenerator gen, SerializerProvider serializers,
|
||||
TypeSerializer typeSer) throws IOException {
|
||||
// 当启用类型信息时,Jackson会调用这个方法
|
||||
try {
|
||||
LazyInitializer lazyInitializer = value.getHibernateLazyInitializer();
|
||||
|
||||
// 获取实际的实体类型名称
|
||||
String entityTypeName = lazyInitializer.getEntityName();
|
||||
Class<?> entityClass = Class.forName(entityTypeName);
|
||||
|
||||
if (lazyInitializer.isUninitialized()) {
|
||||
// 确保输出@class: 'org.hibernate.proxy.HibernateProxy'
|
||||
// 直接使用HibernateProxy.class作为typeId
|
||||
gen.writeStartObject();
|
||||
gen.writeStringField("@class", "org.hibernate.proxy.HibernateProxy");
|
||||
// 对于未初始化的对象,添加代理信息
|
||||
gen.writeStringField("_proxy_", entityTypeName);
|
||||
// 写入ID信息
|
||||
if (lazyInitializer.getIdentifier() != null) {
|
||||
gen.writeFieldName("id");
|
||||
gen.writeObject(lazyInitializer.getIdentifier());
|
||||
}
|
||||
gen.writeEndObject();
|
||||
} else {
|
||||
// 如果代理对象已初始化,获取实际对象
|
||||
Object unwrapped = lazyInitializer.getImplementation();
|
||||
|
||||
// 使用实际对象的类型信息进行序列化
|
||||
// 查找能够处理类型信息的序列化器
|
||||
JsonSerializer<Object> serializer = serializers.findTypedValueSerializer(
|
||||
unwrapped.getClass(), true, null);
|
||||
|
||||
// 委托给实际对象的序列化器处理,确保类型信息正确添加
|
||||
serializer.serializeWithType(unwrapped, gen, serializers, typeSer);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果发生异常,输出最小化信息并添加类型信息
|
||||
// 先写入类型前缀
|
||||
typeSer.writeTypePrefix(gen, typeSer.typeId(value.getClass(), JsonToken.START_OBJECT));
|
||||
// 输出错误信息
|
||||
gen.writeStringField("error", "Failed to serialize Hibernate proxy with type: " + e.getMessage());
|
||||
gen.writeStringField("class", value.getClass().getName());
|
||||
// 写入类型后缀
|
||||
typeSer.writeTypeSuffix(gen, typeSer.typeId(value.getClass(), JsonToken.START_OBJECT));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,30 +5,32 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
|
||||
import com.ecep.contract.model.IdentityEntity;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.BeanDescription;
|
||||
import com.fasterxml.jackson.databind.DeserializationConfig;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
|
||||
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
@@ -43,7 +45,6 @@ public class JacksonConfig {
|
||||
@Bean
|
||||
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
|
||||
ObjectMapper objectMapper = builder.build();
|
||||
|
||||
// 关闭日期时间格式化输出为时间戳,而是输出为ISO格式的字符串
|
||||
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
|
||||
@@ -52,6 +53,9 @@ public class JacksonConfig {
|
||||
// 处理循环引用,启用引用处理功能
|
||||
objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
|
||||
|
||||
// 禁用在遇到空Bean时抛出异常
|
||||
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
||||
|
||||
// 配置Java 8时间模块的序列化格式
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
// LocalDate
|
||||
@@ -63,154 +67,23 @@ public class JacksonConfig {
|
||||
// LocalDateTime
|
||||
javaTimeModule.addSerializer(LocalDateTime.class,
|
||||
new LocalDateTimeSerializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class,
|
||||
new LocalDateTimeDeserializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||
// 自定义LocalDateTime反序列化器,支持多种格式
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());
|
||||
|
||||
objectMapper.registerModule(javaTimeModule);
|
||||
|
||||
// 添加自定义模块,用于处理JPA/Hibernate代理对象
|
||||
SimpleModule proxyModule = new SimpleModule("HibernateProxyModule");
|
||||
// SimpleModule proxyModule = new SimpleModule("HibernateProxyModule");
|
||||
|
||||
// 添加代理对象序列化器,只输出ID字段
|
||||
proxyModule.addSerializer(HibernateProxy.class, new HibernateProxySerializer());
|
||||
// 使用BeanSerializerModifier来处理IdentityEntity类型,避免递归调用
|
||||
proxyModule.setSerializerModifier(new BeanSerializerModifier() {
|
||||
@Override
|
||||
public JsonSerializer<?> modifySerializer(SerializationConfig config,
|
||||
BeanDescription beanDesc,
|
||||
JsonSerializer<?> serializer) {
|
||||
// 只对IdentityEntity类型进行修改
|
||||
if (IdentityEntity.class.isAssignableFrom(beanDesc.getBeanClass()) &&
|
||||
!HibernateProxy.class.isAssignableFrom(beanDesc.getBeanClass())) {
|
||||
return new IdentityEntitySerializer(serializer);
|
||||
}
|
||||
return serializer;
|
||||
}
|
||||
});
|
||||
// // 添加懒加载初始化器序列化器
|
||||
// proxyModule.addSerializer(LazyInitializer.class, new
|
||||
// HibernateProxySerializer());
|
||||
// // 添加Hibernate代理对象反序列化器
|
||||
// proxyModule.addDeserializer(HibernateProxy.class, new
|
||||
// HibernateProxyDeserializer());
|
||||
|
||||
objectMapper.registerModule(proxyModule);
|
||||
// objectMapper.registerModule(proxyModule);
|
||||
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 专门用于处理Hibernate代理对象的序列化器
|
||||
*/
|
||||
private static class HibernateProxySerializer extends StdSerializer<HibernateProxy> {
|
||||
|
||||
protected HibernateProxySerializer() {
|
||||
super(HibernateProxy.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(HibernateProxy value, JsonGenerator gen, SerializerProvider serializers)
|
||||
throws IOException {
|
||||
// 尝试初始化代理对象,如果未初始化则只输出ID
|
||||
try {
|
||||
Object unwrapped = value.getHibernateLazyInitializer().getImplementation();
|
||||
// 检查是否为IdentityEntity实现类
|
||||
if (unwrapped instanceof IdentityEntity) {
|
||||
gen.writeStartObject();
|
||||
gen.writeNumberField("id", ((IdentityEntity) unwrapped).getId());
|
||||
gen.writeEndObject();
|
||||
} else {
|
||||
// 如果不是IdentityEntity,使用默认序列化
|
||||
serializers.defaultSerializeValue(unwrapped, gen);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果初始化失败,只输出ID
|
||||
if (value instanceof IdentityEntity) {
|
||||
gen.writeStartObject();
|
||||
gen.writeNumberField("id", ((IdentityEntity) value).getId());
|
||||
gen.writeEndObject();
|
||||
} else {
|
||||
// 如果不是IdentityEntity,输出对象的基本信息
|
||||
gen.writeStartObject();
|
||||
gen.writeStringField("class", value.getClass().getName());
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于处理IdentityEntity类型的序列化器包装器
|
||||
*/
|
||||
private static class IdentityEntitySerializer extends StdSerializer<Object> {
|
||||
private final JsonSerializer<?> delegate;
|
||||
|
||||
protected IdentityEntitySerializer(JsonSerializer<?> delegate) {
|
||||
super(Object.class);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers)
|
||||
throws IOException {
|
||||
// 对于IdentityEntity对象,如果未初始化,则只输出ID
|
||||
if (value instanceof IdentityEntity && !Hibernate.isInitialized(value)) {
|
||||
gen.writeStartObject();
|
||||
gen.writeNumberField("id", ((IdentityEntity) value).getId());
|
||||
gen.writeStringField("_proxy_", value.getClass().getName());
|
||||
gen.writeEndObject();
|
||||
} else {
|
||||
// 已初始化的实体,使用原始的序列化器进行序列化,避免递归调用
|
||||
((JsonSerializer<Object>) delegate).serialize(value, gen, serializers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于处理对象引用的序列化器,通过检测和管理对象引用避免循环引用问题
|
||||
*/
|
||||
private static class IdentityReferenceSerializer extends StdSerializer<Object> {
|
||||
private final JsonSerializer<?> delegate;
|
||||
private final ThreadLocal<java.util.Set<Object>> visitedObjects = ThreadLocal
|
||||
.withInitial(java.util.HashSet::new);
|
||||
|
||||
protected IdentityReferenceSerializer(JsonSerializer<?> delegate) {
|
||||
super(Object.class);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
// 处理null值
|
||||
if (value == null) {
|
||||
serializers.defaultSerializeNull(gen);
|
||||
return;
|
||||
}
|
||||
|
||||
java.util.Set<Object> visited = visitedObjects.get();
|
||||
try {
|
||||
// 检查对象是否已经被访问过
|
||||
if (visited.contains(value)) {
|
||||
// 如果对象实现了IdentityEntity,只输出ID
|
||||
if (value instanceof IdentityEntity) {
|
||||
gen.writeStartObject();
|
||||
gen.writeNumberField("id", ((IdentityEntity) value).getId());
|
||||
gen.writeStringField("_ref_", "" + ((IdentityEntity) value).getId());
|
||||
gen.writeEndObject();
|
||||
} else {
|
||||
// 对于非IdentityEntity对象,输出对象的toString或hashCode
|
||||
gen.writeString(value.toString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 标记对象为已访问
|
||||
visited.add(value);
|
||||
|
||||
// 使用委托序列化器进行正常序列化
|
||||
((JsonSerializer<Object>) delegate).serialize(value, gen, serializers);
|
||||
} finally {
|
||||
// 清理访问记录
|
||||
visited.remove(value);
|
||||
if (visited.isEmpty()) {
|
||||
visitedObjects.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.ecep.contract.config;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import com.ecep.contract.model.IdentityEntity;
|
||||
import com.ecep.contract.util.HibernateProxyUtils;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.databind.BeanDescription;
|
||||
import com.fasterxml.jackson.databind.DeserializationConfig;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
|
||||
|
||||
/**
|
||||
* Redis缓存配置类,用于配置Redis的缓存管理器和序列化器
|
||||
*/
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class RedisCacheConfig {
|
||||
|
||||
/**
|
||||
* 配置RedisTemplate,使用与JacksonConfig相同的ObjectMapper以确保Hibernate代理对象被正确序列化
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,
|
||||
ObjectMapper objectMapper) {
|
||||
RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(redisConnectionFactory);
|
||||
|
||||
// 设置键的序列化器
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
|
||||
// 使用GenericJackson2JsonRedisSerializer并配置为使用我们自定义的ObjectMapper
|
||||
// 这个ObjectMapper已经包含了处理Hibernate代理对象的序列化器
|
||||
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(
|
||||
objectMapper);
|
||||
template.setValueSerializer(jackson2JsonRedisSerializer);
|
||||
template.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置RedisCacheManager,使用与JacksonConfig相同的ObjectMapper以确保Hibernate代理对象被正确序列化
|
||||
* 并启用类型信息存储以解决LinkedHashMap无法转换为具体类型的问题
|
||||
*/
|
||||
// @Bean
|
||||
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, ObjectMapper objectMapper) {
|
||||
// 创建ObjectMapper的副本,而不是直接修改注入的实例
|
||||
ObjectMapper cacheObjectMapper = objectMapper.copy();
|
||||
|
||||
// 配置ObjectMapper副本以保留类型信息(仅用于Redis缓存)
|
||||
// 使用activateDefaultTyping代替已弃用的enableDefaultTyping
|
||||
cacheObjectMapper.activateDefaultTyping(
|
||||
cacheObjectMapper.getPolymorphicTypeValidator(),
|
||||
ObjectMapper.DefaultTyping.NON_FINAL,
|
||||
JsonTypeInfo.As.PROPERTY);
|
||||
|
||||
// 确保我们的HibernateProxyDeserializer被正确注册
|
||||
// 创建一个新的模块来注册自定义反序列化器
|
||||
SimpleModule proxyModule = new SimpleModule("CacheHibernateProxyModule");
|
||||
// 添加代理对象序列化器,只输出ID字段
|
||||
proxyModule.addSerializer(HibernateProxy.class, new HibernateProxySerializer());
|
||||
proxyModule.addDeserializer(HibernateProxy.class, new HibernateProxyDeserializer());
|
||||
// 使用BeanSerializerModifier来处理IdentityEntity类型,避免递归调用
|
||||
proxyModule.setSerializerModifier(new BeanSerializerModifier() {
|
||||
@Override
|
||||
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
|
||||
JsonSerializer<?> serializer) {
|
||||
Class<?> beanClass = beanDesc.getBeanClass();
|
||||
if (beanClass.isPrimitive()) {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
System.out.println("modifySerializer:" + beanDesc.getBeanClass() + ", serializer:"
|
||||
+ serializer);
|
||||
return serializer;
|
||||
}
|
||||
});
|
||||
proxyModule.setDeserializerModifier(new BeanDeserializerModifier() {
|
||||
@Override
|
||||
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
|
||||
BeanDescription beanDesc,
|
||||
JsonDeserializer<?> deserializer) {
|
||||
Class<?> beanClass = beanDesc.getBeanClass();
|
||||
if (beanClass.isPrimitive()) {
|
||||
return deserializer;
|
||||
}
|
||||
|
||||
System.out.println("modifyDeserializer:" + beanDesc.getBeanClass() + ", deserializer:"
|
||||
+ deserializer);
|
||||
return deserializer;
|
||||
}
|
||||
});
|
||||
cacheObjectMapper.registerModule(proxyModule);
|
||||
|
||||
// 创建Redis缓存配置,使用包含Hibernate代理处理的ObjectMapper副本
|
||||
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
|
||||
.entryTtl(Duration.ofHours(6)) // 设置缓存过期时间为6小时
|
||||
.serializeKeysWith(
|
||||
RedisSerializationContext.SerializationPair
|
||||
.fromSerializer(new StringRedisSerializer()))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
|
||||
new GenericJackson2JsonRedisSerializer(cacheObjectMapper)))
|
||||
.disableCachingNullValues(); // 不缓存null值
|
||||
|
||||
// 创建Redis缓存管理器,为所有缓存名称配置相同的序列化策略以确保类型安全反序列化
|
||||
return RedisCacheManager.builder(redisConnectionFactory)
|
||||
.cacheDefaults(cacheConfiguration)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ public class SecurityConfig {
|
||||
// 注意:根据系统设计,Employee实体中没有密码字段,系统使用IP/MAC绑定认证
|
||||
// 这里使用密码编码器加密后的固定密码,确保认证流程能够正常工作
|
||||
return User.builder()
|
||||
.username(employee.getName())
|
||||
.username(employee.getAccount())
|
||||
.password(passwordEncoder().encode("default123")) // 使用默认密码进行加密
|
||||
.accountExpired(false) // 账户未过期
|
||||
.accountLocked(false) // 账户未锁定
|
||||
|
||||
Reference in New Issue
Block a user