java.lang.IllegalStateException: Failed to deserialize object type

这两天系统时不时的会报这个错误,具体的堆栈信息如下:

java.lang.IllegalStateException: Failed to deserialize object type
        at org.springframework.util.SerializationUtils.deserialize(SerializationUtils.java:75)
        at com.mljr.acs.msettle.core.utils.JedisUtil.get(JedisUtil.java:168)
        at com.xxxx.xxxx.xxxx.web.action.FundTransferBillAction.getCacheAccountList(FundTransferBillAction.java:179)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
         .........................
         .........................
 Caused by: java.lang.ClassNotFoundException: com.xxxx.xxxxx.base.model.customized.FundTransferModel
        at 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.io.ObjectInputStream.resolveClass(ObjectInputStream.java:628)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1620)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
        at org.springframework.util.SerializationUtils.deserialize(SerializationUtils.java:69)
        ... 77 common frames omitted

看到最后的堆栈信息,可以知道根本原因在于没有找到FundTransferModel这个类,看了下报错的代码行,代码是这样的:

FundTransferModel transferBean = (FundTransferModel) jedisUtil.get(allocationUseId);

代码的目的是从redis缓存中获取对象,但是对象的路径并不是错误信息中那个找不到的路径:

//报错类路径
com.xxxx.xxxxx.base.model.customized.FundTransferModel

//实际路径
com.mljr.xxxx.xxxx.core.apps.base.model.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 = pool.getResource();
	return jedis;
 }

//存储对象
public void setex(String key, Object object, int seconds) {
        Jedis jedis = null;
        try {
            jedis = this.getJedis();
            jedis.setex(key.getBytes(), seconds, SerializationUtils.serialize(object));
        } catch (Exception e) {
            logger.error("#设置缓存对象时发生错误", e);
        } finally{
            releaseJedis(jedis);
        }
    }
    
//获取缓存对象
public Object get(String key) {
        Jedis jedis = null;
        try {
            jedis = this.getJedis();
            return  SerializationUtils.deserialize(jedis.get(key.getBytes()));
        } catch (Exception e) {
            logger.error("#获取缓存对象时发生错误", 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
	 */
	@Nullable
	public 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: " + object.getClass(), ex);
		}
		return baos.toByteArray();
	}

	/**
	 * Deserialize the byte array into an object.
	 * @param bytes a serialized object
	 * @return the result of deserializing the bytes
	 */
	@Nullable
	public static Object deserialize(@Nullable byte[] bytes) {
		if (bytes == null) {
			return null;
		}
		try {
			ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
			return ois.readObject();
		}
		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读做了反序列化,进入 ois.readObject() 查看读取对象的过程:

/**
     * 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 object
        int 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串,在读取的时候是不会关注对象的类信息的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值