Java类加载的护城河:深入探究双亲委派机制( 四 )

loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}if (resolve) {resolveClass(c);}return c;}}public static void main(String args[]) throws Exception {// 初始化自定义类加载器,会先初始化父类ClassLoader,// 其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("/Users/lan/data/test");// 在路径/Users/lan/data/test下创建java/lang 几级目录,将java.lang.String.class丢入该目录// 尝试用自己改写类加载机制去加载自己写的java.lang.String.classClass clazz = classLoader.loadClass("java.lang.String");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}}}输出结果:
php复制代码java.lang.SecurityException: Prohibited package name: java.lang at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662) at java.lang.ClassLoader.defineClass(ClassLoader.java:761) at java.lang.ClassLoader.defineClass(ClassLoader.java:642) at com.jvm.classloader.MyClassLoaderTest$MyClassLoader.findClass(MyClassLoaderTest.java:28) at com.jvm.classloader.MyClassLoaderTest$MyClassLoader.loadClass(MyClassLoaderTest.java:53) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at com.jvm.classloader.MyClassLoaderTest$MyClassLoader.main(MyClassLoaderTest.java:72)Exception in thread "main" java.lang.ClassNotFoundException at com.jvm.classloader.MyClassLoaderTest$MyClassLoader.findClass(MyClassLoaderTest.java:31) at com.jvm.classloader.MyClassLoaderTest$MyClassLoader.loadClass(MyClassLoaderTest.java:53) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at com.jvm.classloader.MyClassLoaderTest$MyClassLoader.main(MyClassLoaderTest.java:72)从输出结果,可以看出即使我们自定义的类加载器打破了双亲委派机制,仍然无法成功加载 java.lang.String类 。
这是因为尽管自定义类加载器打破了双亲委派机制,但是由于 Java 虚拟机的安全性设计,它仍然通过检查类名是否以 "java." 开头,禁止加载这些类 。这种安全性设计保障了 Java 的稳定性和安全性,防止恶意代码对核心功能造成损害 。
安全检测的核心源码:

Java类加载的护城河:深入探究双亲委派机制

文章插图
 
另外,java.lang.String.class位于jre/lib/rt.jar 。
Java类加载的护城河:深入探究双亲委派机制

文章插图
 
解压此jar包,即可获取到String.class:
Java类加载的护城河:深入探究双亲委派机制

文章插图
 
写到最后今天,介绍了Java类加载器及双亲委派机制,做下小结:
  1. 类加载与加载器分类: 类的加载是通过类加载器来实现的,主要涉及以下几种加载器:
  • 引导类加载器(BootstrapClassLoader)
  • 扩展类加载器(ExtClassLoader)
  • 应用程序类加载器(AppClassLoader)
  • 自定义类加载器(CustomClassLoader)
  1. 双亲委派机制的作用: JVM采用双亲委派机制来加载类,具体表现为:在加载类时,会首先自底向上地委派给父加载器,检查是否已加载过,若未找到,再自顶向下尝试加载 。这种机制的目的:
  • 沙箱安全机制
  • 类的唯一性保证
  • 类的一致性保证
  1. 自定义加载器的能力: JVM允许用户自定义加载器,通常通过重写ClassLoader的findClass方法来实现 。这样的自定义加载器可以从指定路径中加载特定的类 。
    然而,需要注意的是,自定义加载器虽然能够打破双亲委派机制,但它仍然无法加载以java.开头的核心类库中的类 。
  2. 如果打破双亲委派: 用户可以通过重写类加载方法,不委派给双亲加载,来实现打破双亲委派 。
    实际应用中,例如Tomcat、JDBC等,这些场景需要在共享的类加载环境中加载不同版本的类,因此采取了自定义的类加载机制,打破了传统的双亲委派机制 。

原文链接:
https://juejin.cn/post/7268820544996163639


推荐阅读