当前位置:   article > 正文

Spring-Data-Redis--解决java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxx

java.lang.classcastexception: java.util.linkedhashmap cannot be cast to com.

原文网址:Spring-Data-Redis--解决java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxx_IT利刃出鞘的博客-CSDN博客

简介

说明

本文介绍解决Spring-Data-Redis的“java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxx”报错的方法。

出现的场景

SpringBoot项目中使用Redis来进行缓存。把数据放到缓存中时没有问题,但从缓存中取出来反序列化为对象时报错:“java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxx”。(xxx为反序列化的目标对象对应的类。)

只有这个类里有其他对象字段才会报这个问题,如果这个类里都是初始的类型(比如:Integer,String)则不会报这个错误。

只要用到Redis序列化反序列化的地方都会遇到这个问题,比如:RedisTemplate,Redisson,@Cacheable注解等。

问题复现

业务代码

Controller

  1. package com.example.demo.controller;
  2. import com.example.demo.entity.Result;
  3. import com.example.demo.service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @RestController
  9. @RequestMapping("user")
  10. public class UserController {
  11. @Autowired
  12. private UserService userService;
  13. @GetMapping("page")
  14. public Result page(int pageNo, int pageSize) {
  15. return userService.page(pageNo, pageSize);
  16. }
  17. }

Service

接口

  1. package com.example.demo.service;
  2. import com.example.demo.entity.Result;
  3. public interface UserService {
  4. Result page(int pageNo, int pageSize);
  5. }

实现

  1. package com.example.demo.service.impl;
  2. import com.example.demo.constant.RedisConstant;
  3. import com.example.demo.entity.Result;
  4. import com.example.demo.entity.User;
  5. import com.example.demo.service.UserService;
  6. import org.springframework.cache.annotation.Cacheable;
  7. import org.springframework.stereotype.Service;
  8. import java.util.ArrayList;
  9. import java.util.Arrays;
  10. import java.util.List;
  11. @Service
  12. public class UserServiceImpl implements UserService {
  13. private final List<User> allUsers = Arrays.asList(
  14. new User(1L, "Tony1", 20),
  15. new User(2L, "Tony2", 18),
  16. new User(3L, "Tony3", 30),
  17. new User(4L, "Tony4", 25),
  18. new User(5L, "Tony5", 28)
  19. );
  20. @Override
  21. @Cacheable(cacheNames = "userPageCache")
  22. public Result<List<User>> page(int pageNo, int pageSize) {
  23. String format = String.format("pageNo: %s, pageSize: %s", pageNo, pageSize);
  24. System.out.println("从数据库中读数据。" + format);
  25. int from = (pageNo - 1) * pageSize;
  26. int to = Math.min(allUsers.size(), (pageNo) * pageSize);
  27. List<User> users = new ArrayList<>(allUsers.subList(from, to));
  28. return new Result<List<User>>().data(users);
  29. }
  30. }

Entity

User

  1. package com.example.demo.entity;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @AllArgsConstructor
  7. // 必须要有无参构造函数。因为Redis反序列化为对象时要用到
  8. @NoArgsConstructor
  9. public class User {
  10. private Long id;
  11. private String userName;
  12. private Integer age;
  13. }

Result

  1. package com.example.demo.entity;
  2. import lombok.Data;
  3. @Data
  4. public class Result<T> {
  5. private boolean success = true;
  6. private int code = 1000;
  7. private String message;
  8. private T data;
  9. public Result() {
  10. }
  11. public Result(boolean success) {
  12. this.success = success;
  13. }
  14. public Result<T> success(boolean success) {
  15. Result<T> result = new Result<>(success);
  16. if (success) {
  17. result.code = 1000;
  18. } else {
  19. result.code = 1001;
  20. }
  21. return result;
  22. }
  23. public Result<T> success() {
  24. return success(true);
  25. }
  26. public Result<T> failure() {
  27. return success(false);
  28. }
  29. /**
  30. * @param code {@link ResultCode#getCode()}
  31. */
  32. public Result<T> code(int code) {
  33. this.code = code;
  34. return this;
  35. }
  36. public Result<T> message(String message) {
  37. this.message = message;
  38. return this;
  39. }
  40. public Result<T> data(T data) {
  41. this.data = data;
  42. return this;
  43. }
  44. }

Redis配置代码

  1. package com.example.demo.config;
  2. import com.example.demo.constant.RedisConstant;
  3. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  4. import com.fasterxml.jackson.annotation.JsonTypeInfo;
  5. import com.fasterxml.jackson.annotation.PropertyAccessor;
  6. import com.fasterxml.jackson.databind.ObjectMapper;
  7. import org.springframework.cache.CacheManager;
  8. import org.springframework.cache.annotation.CachingConfigurerSupport;
  9. import org.springframework.cache.annotation.EnableCaching;
  10. import org.springframework.cache.interceptor.KeyGenerator;
  11. import org.springframework.context.annotation.Bean;
  12. import org.springframework.context.annotation.Configuration;
  13. import org.springframework.data.redis.cache.RedisCacheConfiguration;
  14. import org.springframework.data.redis.cache.RedisCacheManager;
  15. import org.springframework.data.redis.connection.RedisConnectionFactory;
  16. import org.springframework.data.redis.core.RedisTemplate;
  17. import org.springframework.data.redis.serializer.*;
  18. import org.springframework.util.StringUtils;
  19. import java.time.Duration;
  20. import java.util.HashMap;
  21. import java.util.Map;
  22. @Configuration
  23. @EnableCaching
  24. public class RedisConfig extends CachingConfigurerSupport {
  25. /**
  26. * 重写缓存Key生成策略。
  27. * 包名+方法名+参数列表。防止缓存Key冲突
  28. */
  29. @Bean
  30. @Override
  31. public KeyGenerator keyGenerator() {
  32. return (target, method, params) -> {
  33. // 存放最终结果
  34. StringBuilder resultStringBuilder = new StringBuilder("cache:key:");
  35. // 执行方法所在的类
  36. resultStringBuilder.append(target.getClass().getName()).append(".");
  37. // 执行的方法名称
  38. resultStringBuilder.append(method.getName()).append("(");
  39. // 存放参数
  40. StringBuilder paramStringBuilder = new StringBuilder();
  41. for (Object param : params) {
  42. if (param == null) {
  43. paramStringBuilder.append("java.lang.Object[null],");
  44. } else {
  45. paramStringBuilder
  46. .append(param.getClass().getName())
  47. .append("[")
  48. .append(String.valueOf(param))
  49. .append("],");
  50. }
  51. }
  52. if (StringUtils.hasText(paramStringBuilder.toString())) {
  53. // 去掉最后的逗号
  54. String trimLastComma = paramStringBuilder.substring(0, paramStringBuilder.length() - 1);
  55. resultStringBuilder.append(trimLastComma);
  56. }
  57. return resultStringBuilder.append(")").toString();
  58. };
  59. }
  60. @Bean
  61. public CacheManager cacheManager(RedisConnectionFactory factory) {
  62. Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>();
  63. RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
  64. .entryTtl(Duration.ofMinutes(5))
  65. .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
  66. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
  67. .disableCachingNullValues();
  68. return RedisCacheManager.builder(factory)
  69. .initialCacheNames(configurationMap.keySet())
  70. .withInitialCacheConfigurations(configurationMap)
  71. .cacheDefaults(config)
  72. .build();
  73. }
  74. @Bean
  75. public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory factory) {
  76. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  77. template.setConnectionFactory(factory);
  78. template.setKeySerializer(keySerializer());
  79. template.setValueSerializer(valueSerializer());
  80. template.setHashKeySerializer(keySerializer());
  81. template.setHashValueSerializer(valueSerializer());
  82. template.afterPropertiesSet();
  83. return template;
  84. }
  85. private RedisSerializer<String> keySerializer() {
  86. return new StringRedisSerializer();
  87. }
  88. private RedisSerializer<Object> valueSerializer() {
  89. Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
  90. new Jackson2JsonRedisSerializer<>(Object.class);
  91. ObjectMapper objectMapper = new ObjectMapper();
  92. objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  93. jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  94. return jackson2JsonRedisSerializer;
  95. }
  96. }

测试

第1次访问(成功)

http://localhost:8080/user/page?pageNo=1&pageSize=2

结果:成功访问,结果或存入Redis

第2次访问(失败)

http://localhost:8080/user/page?pageNo=1&pageSize=2

结果:报错

后端输出:

  1. 2022-01-11 14:59:23.805 ERROR 68468 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.demo.entity.Result] with root cause
  2. java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.demo.entity.Result
  3. at com.sun.proxy.$Proxy56.page(Unknown Source) ~[na:na]
  4. at com.example.demo.controller.UserController.page(UserController.java:18) ~[classes/:na]
  5. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
  6. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_201]
  7. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_201]
  8. at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_201]
  9. at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
  10. at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
  11. ......
  12. at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) [tomcat-embed-core-9.0.46.jar:9.0.46]
  13. at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.46.jar:9.0.46]
  14. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]
  15. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]
  16. at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.46.jar:9.0.46]
  17. at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]

原因分析

SpringBoot 的缓存使用 jackson 来做数据的序列化与反序列化,如果默认使用 Object 作为序列化与反序列化的类型,则其只能识别 java 基本类型,遇到复杂类型时,jackson 就会先序列化成 LinkedHashMap ,然后再尝试强转为所需类别,这样大部分情况下会强转失败。

解决方案

上边是文章的部分内容,为便于维护,全文已转移到此网址:Spring-Data-Redis-解决java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxx - 自学精灵

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/305307
推荐阅读
相关标签
  

闽ICP备14008679号