苍穹外卖debug篇
1.编译错误
在编译初始代码时出现错误
java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'
java.lang.NoSuchFieldError
是一种运行时错误,说明在运行时尝试访问一个不存在的字段。
- 在使用反射机制访问类的字段时,字段名称拼写错误或字段不存在。
- 项目依赖的库或模块版本不一致,导致编译时存在字段但运行时缺失。
- 代码或依赖库发生变更后未重新编译,导致运行时与编译时的类版本不一致。
而我的这个错误是因为JDK版本和Lombok版本冲突,给的原始代码编译是jdk1.8的,但是我IDEA默认编译项目的版本是jdk23。
jdk21之后,Lombok等库无法正确访问内部的Java编译器API会引起这种错误。具体原因是:
Lombok在早期版本中使用反射访问com.sun.tools.javac.tree.JCTree$JCImport类的qualid字段,该字段在Java 21中的类型发生了变化。
在Java 21及更高版本中,qualid字段的类型从JCTree变更为JCFieldAccess。这导致了Lombok无法正确访问该字段,从而抛出NoSuchFieldError异常。
- spring boot - Compilation error after upgrading to JDK 21 - "NoSuchFieldError: JCImport does not have member field JCTree qualid" - Stack Overflow
- 𝑄𝑢𝑒𝑠𝑡𝑖𝑜𝑛Question build fail , "'com.sun.tools.javac.tree.JCTree qualid'" error occur · Issue #9812 · thingsboard/thingsboard · GitHub
- 𝑆𝑜𝑙𝑣𝑒𝑑Solved NoSuchFieldError: Class JCTree$JCImport does not have member field
2.缓存出错
redisTemplate.opsForValue().set(key,list);
在向Redis中写数据时出错,错误信息如下
2024-11-12 18:51:31.605 ERROR 6792 --- [nio-8080-exec-1] 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.ArrayList cannot be cast to java.lang.String] with root cause
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String
at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:36) ~[spring-data-redis-2.7.2.jar:2.7.2]
at org.springframework.data.redis.core.AbstractOperations.rawValue(AbstractOperations.java:128) ~[spring-data-redis-2.7.2.jar:2.7.2]
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:304) ~[spring-data-redis-2.7.2.jar:2.7.2]
根据错误信息,java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String
表明在尝试将一个 ArrayList
类型的数据存储到 Redis 中,但是 RedisTemplate
配置使用了 StringRedisSerializer
作为默认的序列化器。
StringRedisSerializer
只能处理字符串类型的数据,所以使用 opsForValue().set(key, list)
缓存数据到 Redis 时,序列化器会尝试将整个列表转换成一个字符串,所以抛出了 ClassCastException
异常。
我的RedisConfiguration配置类如下,和黑马给的默认写法稍有区别,黑马只设置了redis key的序列化器,而我自作聪明又设置了Value的序列化器,但是设置的序列化器很明显不能将我要缓存的数据进行序列化。
package com.sky.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
log.info("创建redis模板");
RedisTemplate redisTemplate=new RedisTemplate();
//设置Redis连接的工厂对象
redisTemplate.setConnectionFactory(factory);
//分别为Key、Value设置序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(StringRedisSerializer.UTF_8);
redisTemplate.setDefaultSerializer(StringRedisSerializer.UTF_8);
return redisTemplate;
}
}
所以解决这个问题的方法是选择合适的序列化器来处理我要缓存的数据类型。经查,对于列表等复杂数据结构,可以使用 GenericJackson2JsonRedisSerializer
序列化器,它可以处理更复杂的对象。
package com.sky.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
public class RedisConfiguration {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
log.info("创建redis模板");
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置Redis连接的工厂对象
redisTemplate.setConnectionFactory(factory);
// 使用Jackson2实现序列化
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new GenericJackson2JsonRedisSerializer(objectMapper);
// 分别为Key、Value设置序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
使用 GenericJackson2JsonRedisSerializer
作为值和哈希值的序列化器,这样就可以正确地序列化和反序列化包括列表在内的复杂对象。Key继续使用 StringRedisSerializer
进行序列化,因为Redis中的Key通常是字符串形式。然后还要调用 afterPropertiesSet()
方法以确保所有必要的初始化已经完成。