命名空间的补充说明
-
关于命名空间的补充说明
- 子加载器所加载的类可以访问父加载器所加载的类
- 父加载器所加载的类无法访问子加载器所加载的类
- 在jvm中是可以存在多个命名空间的,每个命名空间之间是相互隔离开的。命名空间里面的类时相互不可见的。
- 在运行期,一个java类是由该类的完全限定名(binary name,二进制名)和用于加载该类的定义类加载器(defining loader)所共同决定的。如果同样的名字(即相同的完全限定名)的类是由两个不同的加载器所加载,那么这些类就是不同的,即便.class文件的字节码完全一样,并且从相同的位置加载亦如此。
-
类加载器双亲委托模型的好处
- 可以确保java核心库的类型安全:所有的java应用都至少会引用java.lang.Object类,也就是说在运行期 java.lang.Object这个类会被加载到java虚拟机中;如果这个加载过程是由java应用自己的类加载器所完成的,那么很可能就会在jvm中存在多个版本的java.lang.Object类,而且这些类之间还是不兼容的,相互不可见的(正是命名空间在发挥着作用)。 借助于双亲委托机制,java核心类库中的类的加载工作都是由启动类加载器统一来完成,从而确保了java应用所使用的都是同一个版本的java核心类库,他们之间都是相互兼容的。
- 可以确保java核心类库所提供的类不会被自定义的类所替代
- 不同的类加载器可以为相同名称(binary name)的类创建额外的命名空间。相同名称的类可以并存在java虚拟机中,只需要用不同的类加载器来加载他们即可,不同类加载器所加载的类之间是不兼容的。这就相当于在java虚拟机内部创建了一个又一个相互隔离的java类空间,这类技术在很多框架中都得到了实际应用
-
先有鸡还是先有蛋问题
- 我们知道对于加载器也是java类,那么加载器又是由谁来加载的?
- 内建于JVM的启动类加载器会加载java.lang.ClassLoader以及其他的java平台类。当JVM启动时,一块特殊的机器码会运行,他会加载扩展类加载器与系统类加载器 这块特殊的机器码就叫做启动类加载器(bootstrap)启动类加载器并不是java类,而其他的加载器则是java类。启动类加载器是特定于平台的机器指令,他负责开启整个加载过程。所有类加载器(除了启动类加载器)都被实现为java类,不过总归要有一个组件来加载第一个java类加载器,从而让整个加载过程能够顺利进行下去,加载第一个纯java类加载器就是启动类加载器的职责。启动类加载器还会负责加载供JRE,正常运行所需的基础组件,这包括java.util与java.lang包中的类等等
-
线程上下文类加载器分析和实现
-
线程上下文类加载器
- 当前类加载器(Current Classloader) 用于加载当前类的类加载器
- 每一个类都会使用自己的类加载器(即加载自身的类加载器)来加载其他类(指的是这个类所依赖的其他类)如果ClassX引用了ClassY,那么ClassX的类加载器就会去加载ClassY 前提的ClassY尚未被加载
- 线程上下文类加载器是从JDK1.2开始引入的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader cl)分别用于获取和设置上下文类加载器。
- 如果没有通过setContextClassLoader(ClassLoader cl)进行设置的话,线程将继承其父线程的上下文类加载器。java应用运行时的初始线程的上下文类加载器,在线程中运行的代码可以通过该类加载器来加载该类与资源。
-
线程上下文类加载器的重要性
- SPI(Service Provider Interface)服务提供者接口 面临父加载器看不到子加载器的类的问题父ClassLoader可以使用当前线程Thread.currentThread().getContextLoader()所指定的classloader加载的类这就改变了父ClassLoader不能使用子ClassLoader或是其他没有直接父子关系的ClassLoader加载的类的情况,即改变了双亲委托模型。
- 线程上下文类加载器就是当前线程的Current Classloader
- 在双亲委托模型下,类加载时由下至上的,即下层的类加载器会委托上层进行加载,但是对于SPI来说,有些接口是由java核心库说提供的,而java核心库是由启动类加载器来加载的,而这些接口的实现却来自于不同的jar包(厂商提供),java启动类加载器是不会加载其他来源的jar包,这样传统的双亲委托模型就无法满足SPI的要求,而通过给当前线程设置上下类加载器,就可以由设置的上下文类加载器来实现对于接口实现类的加载。
-
线程上下类类加载的一般使用模式(获取 - 使用 - 还原)
-
伪代码:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); try { // targetTccl 你准备使用的类加载器 Thread.currentThread().setContextClassLoader(targetTccl); // myMethod里面则调用Thread.currentThread().getContextClassLoader(),获取当前线程的上下文类加载器做某些事情 myMethod(); } finally { Thread.currentThread().setContextClassLoader(classLoader); } // 如果一个类由类加载器A加载,那么这个类的依赖类也是由相同的类加载器加载的(如果该依赖类之前没有被加载过的话) // ContextClassLoader的作用就是为了破坏java的类加载的委托机制 // 当高层提供了统一的接口让低层去实现,同时又要在高层加载(或实例化)低层的类时,就必须要通过现场上下文类加载器来帮助高层的ClassLoader找到并加载该类
-
-
网友评论