Java ClassLoader基础及加载不同依赖 Jar 中的公共类
2016-05-21 18:25
555 查看
Java ClassLoader基础及加载不同依赖 Jar 中的公共类
本文主要介绍 ClassLoader 的基础知识,ClassLoader 如何动态加载 Jar,ClassLoader 隔离问题及如何加载不同 Jar 中的公共类。
本文工程开源地址见:Java Dynamic Load Jar@Github,Clone 以后直接以 Java Application去运行 java-dynamic-loader-host 工程即可。
其实本文只是 Android 插件化的一个引子,做过 Android 插件化的同学,可以试试对于 Android Support 包中的 FragmentActivity 和 ActionBarActivity 怎么像一般的 Activity 一样被代理,挺有意思。
1. ClassLoader 的基础知识
无论是 JVM 还是 Dalvik 都是通过 ClassLoader 去加载所需要的类,而 ClassLoader 加载类的方式常称为双亲委托,ClassLoader.java 具体代码如下:
从上面加载类的顺序中我们可以知道,loadClass 会先看这个类是不是已经被 loaded 过,没有的话则去他的 parent 去找,如此递归,称之为双亲委托。
2. 动态加载 Jar
Java 中动态加载 Jar 比较简单,如下:
表示加载 libs 下面的 jar1.jar,其中 parentLoader 就是上面1中的 parent,可以为当前的 ClassLoader。
3. ClassLoader 隔离问题
大家觉得一个运行程序中有没有可能同时存在两个包名和类名完全一致的类?
JVM 及 Dalvik 对类唯一的识别是 ClassLoader id + PackageName + ClassName,所以一个运行程序中是有可能存在两个包名和类名完全一致的类的。并且如果这两个”类”不是由一个 ClassLoader 加载,是无法将一个类的示例强转为另外一个类的,这就是 ClassLoader 隔离。 如 Android 中碰到如下异常
当碰到这种问题时可以通过 instance.getClass().getClassLoader(); 得到 ClassLoader,看 ClassLoader 是否一样。
4. 加载不同 Jar 包中公共类
现在 Host 工程包含了 common.jar, jar1.jar, jar2.jar,并且 jar1.jar 和 jar2.jar 都包含了 common.jar,我们通过 ClassLoader 将 jar1, jar2 动态加载进来,这样在 Host 中实际是存在三份 common.jar,如下图: Class Diagram
我们怎么保证 common.jar 只有一份而不会造成上面3中提到的 ClassLoader 隔离的问题呢,其实很简单,有三种方式:
第一种:我们只要让加载 jar1 和 jar2 的 ClassLoader 的 parent 为同一个 ClassLoader,并且该 ClassLoader 加载过 common.jar,通过上面 1 中我们知道根据双亲委托,最后都会首先被 parentClassLoader加载。
第二种:我们重写 jar1 和 jar2 的 ClassLoader,在 loadClass 函数中我们先去某个含有 common.jar 的 ClassLoader 中 load 即可,其实就是把上面的 parentClassLoader 换掉了而已。
第三种:在生成 jar1 和 jar2 时把 common.jar 去掉,只保留 host 中一份,以 host ClassLoader 为 parentClassLoader 即可。
具体可见代码:JarClassLoader
本文主要介绍 ClassLoader 的基础知识,ClassLoader 如何动态加载 Jar,ClassLoader 隔离问题及如何加载不同 Jar 中的公共类。
本文工程开源地址见:Java Dynamic Load Jar@Github,Clone 以后直接以 Java Application去运行 java-dynamic-loader-host 工程即可。
其实本文只是 Android 插件化的一个引子,做过 Android 插件化的同学,可以试试对于 Android Support 包中的 FragmentActivity 和 ActionBarActivity 怎么像一般的 Activity 一样被代理,挺有意思。
1. ClassLoader 的基础知识
无论是 JVM 还是 Dalvik 都是通过 ClassLoader 去加载所需要的类,而 ClassLoader 加载类的方式常称为双亲委托,ClassLoader.java 具体代码如下:
Java protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(className); if (clazz == null) { try { clazz = parent.loadClass(className, false); } catch (ClassNotFoundException e) { // Don't want to see this. } if (clazz == null) { clazz = findClass(className); } } return clazz; } protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { Class<?> clazz = findLoadedClass(className); if (clazz == null) { try { clazz = parent.loadClass(className, false); } catch (ClassNotFoundException e) { // Don't want to see this. } if (clazz == null) { clazz = findClass(className); } } return clazz; }
从上面加载类的顺序中我们可以知道,loadClass 会先看这个类是不是已经被 loaded 过,没有的话则去他的 parent 去找,如此递归,称之为双亲委托。
2. 动态加载 Jar
Java 中动态加载 Jar 比较简单,如下:
URL[] urls = new URL[] {new URL("file:libs/jar1.jar")}; URLClassLoader loader = new URLClassLoader(urls, parentLoader); URL[] urls = new URL[] {new URL("file:libs/jar1.jar")}; URLClassLoader loader = new URLClassLoader(urls, parentLoader);
表示加载 libs 下面的 jar1.jar,其中 parentLoader 就是上面1中的 parent,可以为当前的 ClassLoader。
3. ClassLoader 隔离问题
大家觉得一个运行程序中有没有可能同时存在两个包名和类名完全一致的类?
JVM 及 Dalvik 对类唯一的识别是 ClassLoader id + PackageName + ClassName,所以一个运行程序中是有可能存在两个包名和类名完全一致的类的。并且如果这两个”类”不是由一个 ClassLoader 加载,是无法将一个类的示例强转为另外一个类的,这就是 ClassLoader 隔离。 如 Android 中碰到如下异常
java.lang.ClassCastException: android.support.v4.view.ViewPager can not be cast to android.support.v4.view.ViewPager java.lang.ClassCastException: android.support.v4.view.ViewPager can not be cast to android.support.v4.view.ViewPager
当碰到这种问题时可以通过 instance.getClass().getClassLoader(); 得到 ClassLoader,看 ClassLoader 是否一样。
4. 加载不同 Jar 包中公共类
现在 Host 工程包含了 common.jar, jar1.jar, jar2.jar,并且 jar1.jar 和 jar2.jar 都包含了 common.jar,我们通过 ClassLoader 将 jar1, jar2 动态加载进来,这样在 Host 中实际是存在三份 common.jar,如下图: Class Diagram
我们怎么保证 common.jar 只有一份而不会造成上面3中提到的 ClassLoader 隔离的问题呢,其实很简单,有三种方式:
第一种:我们只要让加载 jar1 和 jar2 的 ClassLoader 的 parent 为同一个 ClassLoader,并且该 ClassLoader 加载过 common.jar,通过上面 1 中我们知道根据双亲委托,最后都会首先被 parentClassLoader加载。
第二种:我们重写 jar1 和 jar2 的 ClassLoader,在 loadClass 函数中我们先去某个含有 common.jar 的 ClassLoader 中 load 即可,其实就是把上面的 parentClassLoader 换掉了而已。
第三种:在生成 jar1 和 jar2 时把 common.jar 去掉,只保留 host 中一份,以 host ClassLoader 为 parentClassLoader 即可。
具体可见代码:JarClassLoader
相关文章推荐
- Struts2学习笔记
- java的三种构造器
- JAVA集合框架
- RxJava学习(一)
- 关于java命令行在dos窗口下执行class文件时提示“找不到或无法加载类”问题
- Java进阶(二十九)Could not create the view: An unexpected exception was thrown
- Java1.5多线程框架
- Java进阶(二十九)Could not create the view: An unexpected exception was thrown
- Struts2方法调用的三种方式(默认、多方法、通配符)
- java中异常处理
- JSTL与Struts2标签的使用对比==>
- Spring Mvc那点事---(9)Spring Mvc @Component注解
- 在Struts 2通过Spring实现控制反转(IoC)
- JAVA正则表达式 Pattern和Matcher
- JAVA正则表达式:Pattern类与Matcher类详解
- spring错误提示:nested exception is org.xml.sax.SAXParseException; lineNumber: 10; columnNum原因
- Java学习(3)Java语言的环境搭建
- Spring Mvc那点事---(8)Spring Mvc @Resource注解
- Java 多线程
- 全方位剖析Set