转载自:最新内容及最清晰格式请见 http://www.trinea.cn/android/java-loader-common-class/
本文主要介绍 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 具体代码如下:
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);
表示加载 libs 下面的 jar1.jar,其中 parentLoader 就是上面1中的 parent,可以为当前的 ClassLoader。
3. ClassLoader 隔离问题
大家觉得一个运行程序中有没有可能同时存在两个包名和类名完全一致的类?
JVM 及 Dalvik 对类唯一的识别是 ClassLoader id + PackageName + ClassName,所以一个运行程序中是有可能存在两个包名和类名完全一致的类的。并且如果这两个”类”不是由一个 ClassLoader 加载,是无法将一个类的示例强转为另外一个类的,这就是 ClassLoader 隔离。 如 Android 中碰到如下异常
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,如下图:
我们怎么保证 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
大家测试后会发现对于 Java 是正常的,而方式一和方式二对于 Android 却失败,具体原因下次再说吧
相关推荐
在Java编程中,有时我们需要动态地加载外部JAR文件,并执行其中特定的类方法,这在插件系统、模块化应用或动态扩展性需求中尤为常见。本篇将详细讲解如何实现这一目标。 首先,理解JAR(Java Archive)文件是Java...
默认情况下,Java使用`Bootstrap ClassLoader`加载`rt.jar`中的核心类,然后`Extension ClassLoader`加载扩展目录下的类,最后`AppClassLoaer`加载`CLASSPATH`指定的类。这些加载器之间存在父子关系,形成一个层次...
3. **加载**:在Java程序中,`ClassLoader`负责加载`jar`包中的类。默认的系统类加载器会自动处理`classpath`路径下的`jar`文件。如果需要动态加载,可以自定义类加载器,根据需要加载特定的`jar`包。 4. **卸载**...
在Java编程中,有时我们需要在程序运行时动态加载外部的类,这通常涉及到对`.class`文件的调用。Java允许我们通过自定义类加载器或者使用内置的`ClassLoader`来实现这一功能。以下是一份详细的步骤和知识点解释: 1...
JVM(Java虚拟机)采用“双亲委派模型”加载类,即当一个类被加载时,它会首先尝试由启动类加载器(Bootstrap ClassLoader)加载,如果该类不在启动类加载器的路径中,则会委托给扩展类加载器(Extension ...
为了使用这两个jar文件,开发人员需要在Java环境中引入它们,并通过`Class.forName()`或`ClassLoader.loadClass()`来加载对应的类,然后实例化对象并调用其方法。如果它们提供了公共API,那么可以通过查阅API文档或...
首先,rt.jar是Java运行时库的集合,包含了Java标准版API的所有公共类。它由Oracle公司提供,包含了Java SE(Standard Edition)的核心类库,如`java.lang`、`java.util`、`java.io`等包,这些都是编写Java程序不可...
当Java虚拟机或`ClassLoader`实例试图加载不存在的类时,会抛出`ClassNotFoundException`。这通常发生在编译时存在某个类但在运行时找不到该类的情况。 **解决方案:** - **确认类路径:** 确保类路径正确配置,...
JMeter 可以通过 `java.lang.ClassLoader` 类加载外部 jar,并使用 `java.lang.reflect.Method` 来执行方法。你需要确保 JMeter 的 classpath 包含了这个 .jar 文件,或者将其放在 JMeter 的 lib 目录下。 在实际...
5. **热更新机制**:为了实现无感知的更新,可能采用了热更新技术,例如利用ClassLoader重新加载类,或者使用像JRebel这样的工具,允许在不重启应用的情况下更新代码。 6. **安全性考虑**:自动更新过程中需要确保...
类的加载过程遵循经典的Delegation模型,即从父ClassLoader开始查找,找不到再在当前Bundle的ClassLoader中查找。在OSGi中,类的共享是通过Import-Package、Export-Package、Require-Bundle和DynamicImport-Package...
如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 18、error和exception有什么区别? error 表示恢复...
添加你自己的公共指标 vi. 44.6. 指标仓库 vii. 44.7. Dropwizard指标 viii. 44.8. 消息渠道集成 vi. 45. 审计 vii. 46. 追踪(Tracing) i. 46.1. 自定义追踪 viii. 47. 进程监控 i. 47.1. 扩展属性 ii. 47.2. 以...