引用
classloader机制
- 如下图所示,java的classloader是双亲委派机制。会首先从父classloader加载指定的class,如果加载不到才会从子classloader中加载。
- 主要这里的图片主要用于体现classloader的父子关系,实际上实现时并不一定存在继承关系。比如
AppClassLoader
的父classLoader是ExtClassLoader
,但是实际实现时两者都是继承自URLClassLoader
的。
自定义classloader
Animal类&包结构
- 包结构如图:
测试入口
- 输出结果如图所示:
思考1
- 我们定义的classloader真的被运行了吗?
- 实际上如果打一些日志或者是debug进去看下,会发现我们重写的方法并没有被执行,这是java classloader的委派机制搞的鬼,我们可以发现定制的
TinglangClassLoader
的父classloader为URLClassLoader
,在URLClassLoader
中就已经找到了Animal
类了。 - 所以这里可以重写
loadClass
方法来保证我们的classLoader被执行。
思考2
- 我们将代码做一些变种,如下图所示:
- 执行结果直接抛异常了
- 这是由于
Animal
类来源于两个不同的类加载器。如果只是需要执行Animal
中的say
方法的话,直接反射调用即可,如下图所示:
思考3
- 我们再将代码做一下变化,如下图所示,
Animal
作为一个普通内部类来实现,如下图所示: - 可以看到代码已经提示无法实例化内部类了,这个是由于要实例化非静态的内部类对象,必须要先实例化外部类的对象,可以采用下面的方法来解决:
- ps:这里内部类有
public
修饰符~
思考4
- tomcat也是定义了自己的classloader,那么为啥不用jvm提供的classloader呢?
- 结合相关的资料主要是三个方面的目的:
- webapp隔离:由于各个webapp中的class和lib文件需要相互隔离,不能出现一个应用中加载的类库会影响到另一个应用的情况。
- 安全性:与jvm相同,tomcat也期望使用单独的classloader去装载tomcat自身的类库,以免其他恶意或者无意的破坏。
- 热部署:tomcat修改文件可以不用重启自动装载类库,这个点后面我们会单独抽取示例。
思考5
- 既然ExtClassLoader是读取特定目录下的class文件,那么如果我将自定义的class文件移到
$JAVA_HOME/jre/lib/ext/
目录下是不是就能够达到指定classloader加载类的目标呢? - 这个点是可以的,但是必须要求是jar。
思考6
- 类加载器常见的用途有类的隔离和热替换,类的隔离非常好理解,那么热替换呢?
- 仍然以我们之前的代码为例,如下图所示:
- 在程序运行过程中更新并编译
Animal
类中say方法 - 可以看到运行结果如图所示:
- ps:注意第一张图中的红色框框部分,如果修改为
Thread.currentThread().getContextClassLoader()
会发现实际上不会起作用,这是由于要想实现同一个类的不同版本的共存,这些不同的版本必须由不同的类加载器进行加载,因此就不能把这些类的加载工作委托给类加载器来完成,因为它们只有一份。