这两天系统时不时的会报这个错误,具体的堆栈信息如下:
java.lang.IllegalStateException: Failed to deserialize object typeat org.springframework.util.SerializationUtils.deserialize(SerializationUtils.java:75)at com.mljr.(JedisUtil.java:168)web.CacheAccountList(FundTransferBillAction.java:179)flect.NativeMethodAccessorImpl.invoke0(Native Method)flect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)flect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at flect.Method.invoke(Method.java:498)..................................................Caused by: java.lang.ClassNotFoundException: del.customized.FundTransferModelat org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1332)at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1166)at java.lang.Class.forName0(Native Method)at java.lang.Class.forName(Class.java:348)at java.solveClass(ObjectInputStream.java:628)at java.adNonProxyDesc(ObjectInputStream.java:1620)at java.adClassDesc(ObjectInputStream.java:1521)at java.adOrdinaryObject(ObjectInputStream.java:1781)at java.adObject0(ObjectInputStream.java:1353)at java.adObject(ObjectInputStream.java:373)at org.springframework.util.SerializationUtils.deserialize(SerializationUtils.java:69)... 77 common frames omitted
看到最后的堆栈信息,可以知道根本原因在于没有找到FundTransferModel这个类,看了下报错的代码行,代码是这样的:
FundTransferModel transferBean = (FundTransferModel) (allocationUseId);
代码的目的是从redis缓存中获取对象,但是对象的路径并不是错误信息中那个找不到的路径:
//报错类路径
del.customized.FundTransferModel//实际路径
del.customized.FundTransferModel
因为之前服务做过一次优化,重新调整了类路径,调整前的路径确实是报错的那个路径。
所以原因在于:
保存到redis中的对象是以报错类路径保存的,因为没有设置过超时时间,调整了类路径后,重新取出时转类型时就有问题了。后来把相关缓存内容清掉,重新写入了一次,之后就恢复正常了。
下面看下redis写入的实现:
private JedisPool pool ;/***最大连接数*/
public static final int CONFIG_MAX_ACTIVE = 500;/*** 最大空闲连接数,-1 表示无限制*/public static final int CONFIG_MAX_IDLE = -1;/*** 取一个连接的最长阻塞时间, milliseconds. 此处设置为10秒*/public static final int CONFIG_MAX_WAITE_MILLISECOND = 1000; // 10 seconds.//redis 连接池相关参数配置
private Jedis getJedis() {if (pool == null) {config = new JedisPoolConfig();config.setMaxTotal(CONFIG_MAX_ACTIVE);config.setMaxIdle(CONFIG_MAX_IDLE);config.setMaxWaitMillis(CONFIG_MAX_WAITE_MILLISECOND);config.setBlockWhenExhausted(false);JedisPool pool = new JedisPool(config, host, port, timeout, password);}jedis = Resource();return jedis;}//存储对象
public void setex(String key, Object object, int seconds) {Jedis jedis = null;try {jedis = Jedis();jedis.Bytes(), seconds, SerializationUtils.serialize(object));} catch (Exception e) {("#设置缓存对象时发生错误", e);} finally{releaseJedis(jedis);}}//获取缓存对象
public Object get(String key) {Jedis jedis = null;try {jedis = Jedis();return SerializationUtils.(Bytes()));} catch (Exception e) {("#获取缓存对象时发生错误", e);return null;} finally{releaseJedis(jedis);}}
在存储前,用了序列化工具类对对象进行序列化然后存储,取出的时候再进行一次反序列化,序列化的工具类是Spring提供的工具类,实现如下所示:
SerializationUtils.java
public abstract class SerializationUtils {/*** Serialize the given object to a byte array.* @param object the object to serialize* @return an array of bytes representing the object in a portable fashion*/@Nullablepublic static byte[] serialize(@Nullable Object object) {if (object == null) {return null;}ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);try {ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(object);oos.flush();}catch (IOException ex) {throw new IllegalArgumentException("Failed to serialize object of type: " + Class(), ex);}ByteArray();}/*** Deserialize the byte array into an object.* @param bytes a serialized object* @return the result of deserializing the bytes*/@Nullablepublic static Object deserialize(@Nullable byte[] bytes) {if (bytes == null) {return null;}try {ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));adObject();}catch (IOException ex) {throw new IllegalArgumentException("Failed to deserialize object", ex);}catch (ClassNotFoundException ex) {throw new IllegalStateException("Failed to deserialize object type", ex);}}}
就是用 ByteArrayOutputStream 写做了序列化,用ByteArrayInputStream读做了反序列化,进入 adObject() 查看读取对象的过程:
/*** Read an object from the ObjectInputStream. The class of the object, the* signature of the class, and the values of the non-transient and* non-static fields of the class and all of its supertypes are read.* Default deserializing for a class can be overridden using the writeObject* and readObject methods. Objects referenced by this object are read* transitively so that a complete equivalent graph of objects is* reconstructed by readObject.* * 注释含义:* 从ObjectInputStream读取一个对象。* 对象所属类,类的签名,所有非transient修饰的变量,以及超类的相关信息都会被读取。* 类的默认的反序列化方法可以使用writeObject 和 readObject 方法覆盖。* 被该对象引用的对象能够等价的被readObject重构。** <p>The root object is completely restored when all of its fields and the* objects it references are completely restored. At this point the object* validation callbacks are executed in order based on their registered* priorities. The callbacks are registered by objects (in the readObject* special methods) as they are individually restored.** <p>Exceptions are thrown for problems with the InputStream and for* classes that should not be deserialized. All exceptions are fatal to* the InputStream and leave it in an indeterminate state; it is up to the* caller to ignore or recover the stream state.** @throws ClassNotFoundException Class of a serialized object cannot be* found.* @throws InvalidClassException Something is wrong with a class used by* serialization.* @throws StreamCorruptedException Control information in the* stream is inconsistent.* @throws OptionalDataException Primitive data was found in the* stream instead of objects.* @throws IOException Any of the usual Input/Output related exceptions.*/public final Object readObject()throws IOException, ClassNotFoundException{if (enableOverride) {return readObjectOverride();}// if nested read, passHandle contains handle of enclosing objectint outerHandle = passHandle;try {Object obj = readObject0(false);handles.markDependency(outerHandle, passHandle);ClassNotFoundException ex = handles.lookupException(passHandle);if (ex != null) {throw ex;}if (depth == 0) {vlist.doCallbacks();}return obj;} finally {passHandle = outerHandle;if (closed && depth == 0) {clear();}}}
由第一段注释可知,在反序列化的时候,即 readObject 时,不只会读取对象的所有属性字段,对象的类信息和超类信息也会被读取。改造前后的对象(FundTransferModel)的属性值虽然相同,但是类信息是不同的,所以在转换类型的时候就会抛出 ClassNotFoundException 异常。
总结:
redis 存储信息时,如果只保留对象的部分信息,尽量不要保存对象本身。如需保存对象所有数据,可以保存对象的JSON串。上面的问题,如果是保存的JSON串,在读取的时候是不会关注对象的类信息的。
本文发布于:2024-02-03 04:33:38,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170690602048673.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |