类的双亲委托模型&相关动作&命名空间&上下文类加载器
有一个类(A.class)需要类加载器去加载,如果有父类,先让父类去加载,如此向上追溯,知道根 类加载器,然后根类加载器尝试去加载,加载成功则结束,加载失败,又往下,一层层的尝试去加载,最终如果都没有加载成功,则报错 classnotfound; 但是并不是所有的jvm都是这样,hotspot遵循这样规则。
一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,因为java最早就是为嵌入式系统而设计的,内存宝贵,这是一种可以理解的机制,而用到时再加载这也是java动态性的一种体现
数组class对象是jvm虚拟机在运行时动态创建的
不同类加载器的命名空间关系
例子:
// 初始化两个自定义类加载器
ClassLoader cl1 = new MyClassLoader();
ClassLoader cl2 = new MyClassLoader();
Class<?> clazz1 = cl1.loadClass("com.example.MyClass1")
Class<?> clazz2 = cl2.loadClass("com.example.MyClass1")
System.out.println(clazz1==clazz2) //false
在上述例子中clazz1和clazz2就属于两个不同命名空间中相同的类,虽然他们是加载的相同的类,但是在JVM中他们并不相同。
线程上下文类加载器是从JDK1.2开始引入的,类Thread中的
getContextClassLoader()
与setContextClassLoader(Classloader c)
分别用来获取和设置上下文类加载器。 如果没有通过setContextClassLoader(Classloader c)
来进行设置的话,线程将继承其弗雷德上下文类加载器。Java应用运行时的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过该类加载器来类与资源。
父ClassLoader可以使用当前线程的
Thread.currentThread().getContextClassLoader()
所指定的ClassLoader加载类。这就改变了父ClassLoader不能使用子ClassLoader或其他没有直接父子关系的ClassLoader加载的类的情况,即改变了双亲委托模型。 在双亲委托模型下,类的加载是自下而上的,即下层的类加载器会委托上层进行加载。但是对于SPI(Service Provider Interface)
来说,有些接口是Java核心库所提供的,而Java核心库是有类加载来进行加载的,而这些接口的实现确是来自不同厂商提供的Jar包,Java的启动类加载器默认是不同加载其他来源的Jar包,这样传统的双亲委托模型就无法满足SPI的要求,而通过给当前线程设置上线文类加载器,就可以由设置的上下文类加载器来实现对于接口类的加载。
获取 - 使用 - 还原
ClassLoader loader = Thread.currentThread().getContextClassLoader(); //获取
try {
//使用
Thread.currentThread().setContextClassLoader(targetTccl);
myMethod();
}finally {
Thread.currentThread().setContextClassLoader(loader); //还原
}
类加载器双亲委托模型的好处: