Java类加载器的底层原理( 二 )

package java.lang;public class String {//static{System.out.println("我是自定义的String类的静态代码块");}//错误: 在类 java.lang.String 中找不到 main 方法public static void main(String[] args) {System.out.println("hello,String");}}//因为加载的是核心类的String , 在String中找不到main方法public class StringTest {public static void main(String[] args) {java.lang.String str = new java.lang.String();//无输出StringTest test = new StringTest();System.out.println(test.getClass().getClassLoader());}}package java.lang;public class ShkStart {//错误:java.lang.SecurityException: Prohibited package name: java.langpublic static void main(String[] args) {System.out.println("hello!");}}//因为java.lang包由引导类加载器加载 , 引导类中并没有此类 , 为了安全引导类破坏双亲委派模型:较大规模的破坏双亲委派模型的有3种:

  • 由于双亲委派模型是在JDK1.2之后才引入的 , 所以在JDK1.2之前是不符合双亲委派模型的:
  • ClassLoader这类在JDK1.0开始有存在的 , 在JDK1.2之前 , ClassLoader中是通过私有方法loadClassInternal()去调用自己内部的loadClass() 。 为了满足双亲委派以及向下兼容 , 在JDK1.2后的ClassLoader类中 , 又为该类添加了protected的findClass()方法 , JDK1.2之后就不推荐通过覆盖重写loadClass()方法了 , 而是在新添加的findClass()方法中书写自己的类加载逻辑 , 若loadClass()方法中的父类加载失败则会调用自己的findClass()方法 。
  • 由于双亲委派模型的旨意是越核心的类越由高层的加载器所加载(上文提到过的String类) , 倘若这些核心类要去调用用户的基础类 , 例如JNDI服务(是对 资源进行集中管理和查找 , 它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者(SPI,Service Provider Interface)的代码 , 但启动类加载器不可能"认识"这些代码)为了解决调用问题 , 设计了一个线程上下文类加载器(Thread Context ClassLoader) , 这个类加载器可以通过java.lang.Thread类的setContextClassLoaser()方法进行设置 , 如果创建线程时还未设置 , 它将会从父线程中继承一个 , 如果在应用程序的全局范围内都没有设置过的话 , 那这个类加载器默认就是应用程序类加载器 。 有了这个上下文类加载器 , 就可以去加载所需要的SPI代码 , 实际上就是从父类加载器去请求子类加载器去完成类的加载驱动 , 违背了双亲委派的一般性规则 。 Java中所有涉及SPI的加载动作基本上都采用这种方式 , 例如JNDI、JDBC、JCE、JAXB和JBI等 。
  • 为了满足"热部署"、"动态部署"等功能而导致的 。 在OSGi(动态模块技术)环境下 , 类加载器不再是双亲委派模型中的树状结构 , 而是进一步发展为更加复杂的网状结构 , 当收到类加载请求时 , OSGi将按照顺序进行类搜索
关于类加载器的一些补充1. JVM中判断一个类是否是同一个类有两个必要条件:
  • 这两个类的全限定名要一致
  • 这两个类被同一个类加载器加载 。
2. 对类加载器的引用:
  • JVM必须知道一个类型是由引导类加载器(启动类加载器)加载的还是由用户类加载器加载的 。
  • 如果一个类是由用户类加载器所加载的 , 那么JVM会将这个类加载器的一个引用作为类信息的一部分保存在方法区 。
  • 当解析一个类型到另外一个类型的引用的时候 , JVM需要保证这两个类型的类加载器是相同的 。 3. 类的主动使用和被动使用主动使用:创建类的实例访问某个类或接口的静态变量 , 或者对该静态变量赋值调用类的静态方法反射初始化一个类的子类JVM启动时被标明为启动类的类JDK 7 开始提供的动态代理:java.invoke.MethodHandle实例的解析结果 , REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化、则初始化除以上7种情况 , 其他使用Java类的方式都为被动使用 , 被动使用不会导致类的初始化 。
作者:龚生
【Java类加载器的底层原理】出处: