spring-全量自定义-类型安全转换系统

类型安全转换系统包含四个个核心接口:
Converter, //简单转换器接口
GenericConverter, //通用转换器接口
ConversionService, //转换服务
ConverterRegistry //转换器注册

他们之间的逻辑关系:
通过ConverterRegistry向系统中注入实现了Converter,GenericConverter接口的转换器,然后通过ConversionService对外提供类型转换服务.

核心实现类GenericConversionService:通用类型转换服务类,实现了ConverterRegistry和ConversionService接口:

两个核心的组件:

private final Converters converters = new Converters();//存储所有的转换器
//以转换对为key,支持该转换对转换器为值保存
	private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64);

其中把所有的Converter接口实现,通过适配器,转换成GenericConverter;

现在看看系统支持的转换类把:
转换类很多,位于org.springframework.core.convert.support包下.

从spring这个设计结构中我们可以看出一般功能的设计方案:
先按照功能进行接口设计,然后基于接口,实现类考虑如何满足接口功能,进行结构和算法层面的设计,最后进行编码.

java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:36) ~[spring-data-redis-2.6.2.jar:2.6.2] at org.springframework.data.redis.core.script.DefaultScriptExecutor.keysAndArgs(DefaultScriptExecutor.java:112) ~[spring-data-redis-2.6.2.jar:2.6.2] at org.springframework.data.redis.core.script.DefaultScriptExecutor.lambda$execute$0(DefaultScriptExecutor.java:60) ~[spring-data-redis-2.6.2.jar:2.6.2] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:223) ~[spring-data-redis-2.6.2.jar:2.6.2] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:190) ~[spring-data-redis-2.6.2.jar:2.6.2] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:177) ~[spring-data-redis-2.6.2.jar:2.6.2] at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:58) ~[spring-data-redis-2.6.2.jar:2.6.2] at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:52) ~[spring-data-redis-2.6.2.jar:2.6.2] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:344) ~[spring-data-redis-2.6.2.jar:2.6.2] at com.hmdp.service.impl.VoucherOrderServiceImpl.seckillVoucher(VoucherOrderServiceImpl.java:102) ~[classes/:na] at com.hmdp.service.impl.VoucherOrderServiceImpl$$FastClassBySpringCGLIB$$ba28bb10.invoke(<generated>) ~[classes/:na] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.15.RELEASE.jar:5.2.15.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) ~[spring-aop-5.2.15.RELEASE.jar:5.2.15.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.15.RELEASE.jar:5.2.15.RELEASE] at org.s
04-01
<think>嗯,用户遇到了在Spring Data Redis中由于Long类型被强制转换为String导致的ClassCastException异常。我需要仔细分析这个问题,并提供有效的解决方案。 首先,用户提到的是ClassCastException,具体是Long转String的问题。这通常发生在序列化或反反序列化过程中类型不匹配的时候。在Spring Data Redis中,默认的序列化器可能使用了JdkSerializationRedisSerializer,这会导致存储的数据带有类型信息,当其他客户端(如命令行或不同配置的应用)以字符串形式存储数据时,读取时就会抛出类型转换异常。 接下来,我需要考虑如何解决这个问题。常见的解决方案是统一序列化方式。例如,使用StringRedisTemplate代替默认的RedisTemplate,因为StringRedisTemplate使用的是StringRedisSerializer,这样可以确保键和值都以字符串形式存储,避免了类型问题。同时,如果用户需要存储对象,可以配置自定义的序列化器,比如Jackson2JsonRedisSerializer,并确保对象实现了Serializable接口。 另外,可能还需要检查代码中是否有手动类型转换的地方,比如在获取缓存数据时直接进行强制类型转换,这应该改为使用类型安全的方法,如类型推断或Optional处理。同时,提醒用户维护数据一致性,如果Redis中已有旧数据,可能需要清除或迁移数据,避免新旧序列化方式冲突。 最后,生成相关问题,帮助用户深入理解序列化机制和相关配置,比如不同序列化器的区别、如何存储复杂对象、处理多种数据类型,以及常见的异常处理方式。</think>### 问题分析 出现`java.lang.ClassCastException: Long cannot be cast to String`的根本原因是**Redis序列化方式与数据类型不匹配**。在Spring Data Redis中,当使用默认的`JdkSerializationRedisSerializer`时,可能因数据类型隐式转换引发异常[^1]。 --- ### 分步解决方案 #### 1. 统一序列化配置 ```java @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 使用String序列化器处理KEY template.setKeySerializer(new StringRedisSerializer()); // 使用JSON序列化器处理VALUE template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } } ``` *说明*:通过配置统一的JSON序列化器,确保所有类型数据都能正确转换 #### 2. 使用StringRedisTemplate(简单场景) ```java @Autowired private StringRedisTemplate stringRedisTemplate; // 存储操作 stringRedisTemplate.opsForValue().set("counter", "100"); // 读取操作(避免直接类型转换) Long value = Long.parseLong(stringRedisTemplate.opsForValue().get("counter")); ``` #### 3. 数据类型校验(防御性编程) ```java Object value = redisTemplate.opsForValue().get("key"); if (value instanceof String) { String strValue = (String) value; } else if (value instanceof Long) { Long longValue = (Long) value; } ``` #### 4. 数据迁移(存量数据处理) ```bash # 使用redis-cli转换现有数据 redis-cli KEYS "*" | xargs -I key redis-cli GET key | xargs -I val redis-cli SET key val ``` --- ### 关键点总结 | 方案 | 适用场景 | 优点 | 缺点 | |------|---------|------|------| | 统一序列化 | 新项目/全量改造 | 一劳永逸 | 需兼容历史数据 | | StringRedisTemplate | 简单字符串存储 | 轻量易用 | 不支持复杂对象 | | 数据类型校验 | 临时修复 | 快速实施 | 存在性能损耗 | --- ### 扩展建议 1. 使用`@TypeAlias`注解为复杂类型添加类型别名 2. 通过`RedisCacheConfiguration`配置缓存序列化策略 3. 使用`HashOperations`代替简单KV存储结构化数据
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值