最近在做的项目需要用到memcached,java客户端就选了国产的xmemcached。有个很奇怪的问题,序列化自定义对象是没问题,反序列化对象时就抛出ClassNotFoundException,重新检查这个类,确实存在。
没办法,向google求救,原来,把xmemcached这个包放在tomcat/lib下就会抛出ClassNotFoundException,而放在webapps/[app]/WEB-INF/lib下就正常。估计是不同类加载器的问题。
既然找到了原因,就容易了,打开xmemcached源代码,找到这个类BaseSerializingTranscoder的deserialize方法,用的是jdk提供的ObjectInputStream,又查了jdk文档,只覆盖ObjectInputStream的resolveClass就可以从别的类加载器加载类了。
附上修改后的源代码:
public class CustomerSerializingTranscoder extends SerializingTranscoder { public CustomerSerializingTranscoder() { } @Override protected Object deserialize(byte[] in) { Object rv = null; ByteArrayInputStream bis = null; ObjectInputStream is = null; try { if (in != null) { bis = new ByteArrayInputStream(in); is = new ObjectInputStream(bis) { @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { try { return Thread.currentThread().getContextClassLoader().loadClass(desc.getName()); } catch (ClassNotFoundException e) { return super.resolveClass(desc); } } }; rv = is.readObject(); } } catch (IOException e) { log.error("Caught IOException decoding " + in.length + " bytes of data", e); } catch (ClassNotFoundException e) { log.error("Caught CNFE decoding " + in.length + " bytes of data", e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } if (bis != null) { try { bis.close(); } catch (IOException e) { } } } return rv; } }
然后在spring配置文件里指定这个自定义解码器就行了:
<bean id="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder"> ... <property name="transcoder"> <bean class="xxx.CustomerSerializingTranscoder" /> </property> </bean>
这样,不管xmemcached是放在tomcat/lib目录下还是webapps/[app]/WEB-INF/lib目录,都不会抛出ClassNotFoundException了