我是靠谱客的博主 魁梧帅哥,这篇文章主要介绍反序列化 动态加载jar的里的类报ClassNotFoundException解决办法,现在分享给大家,希望可以做个参考。

1.背景

自己在写一个RPC框架时,碰到第一个麻烦就是做动态加载加载jar包后,在进行反序列化(不要吐槽为啥用java原生的序列化方案,一步一步来,框架写完能跑后在优化)时报CNF错误,当时感觉应该是原生的序列化方案中使用的ClassLoader是应用加载器AppCloassLoader,而我使用的URLClassLoader加载的外部jar包,导致没有找到。

关于java的类加载器,如果知道双亲委托机制就可以往下接着看了,如果不了解,我帮大家找了一篇。
双亲委托机制总的来说就是,如果一个classloader要加载一个类,先问问他的父级classloader没有加载这个类,父级也会在去问它的父级,直至bootstrap classloader,如果其父级加载器无法加载该类,才会交由它本身加载。

2.解决方案

java反序列化代码一般为下面这样

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
public static Object deserialize(byte[] bytes) { ByteArrayInputStream bais = null; ObjectInputStream ois = null; try { // 反序列化 bais = new ByteArrayInputStream(bytes); ois = new ObjectInputStream(bais); return ois.readObject(); } catch (Exception e) { log.error("反序列化错误", e); } return null; }

查看ObjectInputStream源码找到了类加载的地方

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String name = desc.getName(); try { return Class.forName(name, false, latestUserDefinedLoader()); } catch (ClassNotFoundException ex) { Class<?> cl = primClasses.get(name); if (cl != null) { return cl; } else { throw ex; } } }

可以看到Class.forName中使用的是AppClassLoader,解决方案其实就是复写该方法,使用我们动态加载的ClassLoader

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ObjectInputStreamWithLoader extends ObjectInputStream { private ClassLoader loader; public ObjectInputStreamWithLoader(InputStream in, ClassLoader loader) throws IOException, StreamCorruptedException { super(in); if (loader == null) { throw new IllegalArgumentException("Illegal null argument to ObjectInputStreamWithLoader"); } this.loader = loader; } /** * Use the given ClassLoader rather than using the system class */ @SuppressWarnings("rawtypes") protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { String cname = classDesc.getName(); return loader.loadClass(cname); } }

可以看到在实例化该类的时候,需传入一个classloader

动态加载我使用的是URLClassLoader,代码如下

复制代码
1
2
3
4
5
URL[] urls = new URL[2]; urls[0] = new URL("file:///D:/jar/a.jar"); urls[1] = new URL("file:///D:/jar/a.impl.jar"); URLClassLoader classLoader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());

3.利用Hessian序列化

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static byte[] serialize(Object obj) throws IOException { if (obj == null) throw new NullPointerException(); ByteArrayOutputStream os = new ByteArrayOutputStream(); HessianOutput ho = new HessianOutput(os); ho.writeObject(obj); return os.toByteArray(); } public static Object deserialize(byte[] by, ClassLoader classLoader) throws IOException { if (by == null) throw new NullPointerException(); ByteArrayInputStream is = new ByteArrayInputStream(by); ClassLoader old = null; if (classLoader != null) { old = Thread.currentThread().getContextClassLoader(); // 切换当前线程classloader,保证动态加载的类不会报CNF Thread.currentThread().setContextClassLoader(classLoader); } HessianInput hi = new HessianInput(is); Object obj = hi.readObject(); if (classLoader != null) { Thread.currentThread().setContextClassLoader(old); } return obj; }

在反序列时,修改其当前线程上下文类加载器,保证能加载到该类。

复制代码
1
2
最近打算从头开始写一个RPC框架,欢迎有兴趣的同学一块交流 https://github.com/yangzhenkun/krpc

最后

以上就是魁梧帅哥最近收集整理的关于反序列化 动态加载jar的里的类报ClassNotFoundException解决办法的全部内容,更多相关反序列化内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部