java类加载以及双亲委派机制

简介: web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已,经是司空见惯的事情,web容器要支持jsp的修改后不用重启。

java类加载以及双亲委派机制

java类加载运行过程



类加载过程 (classLoader.loadClass)



加载: 在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的

java.lang.Class对象,作为方法区这个类的各种数据的访问入口


验证: 校验字节码文件的正确性


准备: 给类的静态变量分配内存,并赋予默认值


解析: 将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用。(将符号转成内存地址的过程)


初始化: 对类的静态变量初始化为指定的值,执行静态代码块


类加载器和双亲委派机制

上面的类加载过程主要是由类加载器来实现的,java里有如下几种加载器


1.引导类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等

2.扩展类加载器(ExtClassLoader): 负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包

3.应用程序类加载器(AppClassLoader): 负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类

4.自定义加载器: 负责加载用户自定义路径下的类包


常见的类加载器


public static void main(String[] args) {
    //引导类加载器
    System.out.println(String.class.getClassLoader());
    //应用程序类加载器
    System.out.println(TestClassLoader.class.getClassLoader());  
    //扩展类加载器
    System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader()); 
}


null
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@77459877


结论:
  1. 由于引导类加载器是c++实现,java层面是获取不到引导类加载器的对象,所以String类的加载器为null
  2. AppClassLoader和ExtClassLoader都是com.misc.Launcher的静态类


关于ClassLoader的 parent 属性
public abstract class ClassLoader {
    //ClassLoader里面定义了一个parent属性,用于标识父类加载器
    private final ClassLoader parent;
}


关于URLClassLoader



1.URLClassLoader继承SecureClassLoader,

2.SecureClassLoader继承ClassLoader

3.AppClassLoader和ExtClassLoader都继承了URLClassLoader


类加载过程中Launcher.getLauncher()


public class Launcher {
    private static URLStreamHandlerFactory factory = new Factory();
    //具体new Launcher()方法
    private static Launcher launcher = new Launcher();
    private static String bootClassPath = System.getProperty("sun.boot.class.path");
    //默认的类加载器,在初始化的时候loader的值被赋予应用程序类加载器
    private ClassLoader loader;
    private static URLStreamHandler fileHandler;
  //返回launcher对象
    public static Launcher getLauncher() {
        return launcher;
    }
}
new Launcher()
  public Launcher() {
        ExtClassLoader extClassLoader;
        try {
            //获取扩展类加载器
            extClassLoader = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            //应用程序类加载器
            this.loader = Launcher.AppClassLoader.getAppClassLoader(extClassLoader);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }

        Thread.currentThread().setContextClassLoader(this.loader);
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                } catch (InstantiationException var6) {
                } catch (ClassNotFoundException var7) {
                } catch (ClassCastException var8) {
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

关于new URLClassLoader()


  public URLClassLoader(URL[] urls, ClassLoader parent,
                          URLStreamHandlerFactory factory) {
      //指定父类加载器
        super(parent);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        acc = AccessController.getContext();
        ucp = new URLClassPath(urls, factory, acc);
    }

获取扩展类加载器



  public static ExtClassLoader getExtClassLoader() throws IOException {
            if (instance == null) {
                Class var0 = ExtClassLoader.class;
                synchronized(ExtClassLoader.class) {
                    if (instance == null) {
                        //往下走
                        instance = createExtClassLoader();
                    }
                }
            }

            return instance;
  }


  private static ExtClassLoader createExtClassLoader() throws IOException {
            try {
                return (ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() {
                    public ExtClassLoader run() throws IOException {
                        //获取系统扩展类jar包的文件
                        File[] var1 = Launcher.ExtClassLoader.getExtDirs();
                        int var2 = var1.length;

                        for(int var3 = 0; var3 < var2; ++var3) {
                            MetaIndex.registerDirectory(var1[var3]);
                        }

                        return new ExtClassLoader(var1);
                    }
                });
            } catch (PrivilegedActionException var1) {
                throw (IOException)var1.getException();
            }
  }


    public ExtClassLoader(File[] extJarFiles) throws IOException {
          //初始化父类URLClassLoader的相关逻辑,
          //其中第二个参数为指定父加载器,此处父加载器应该是引导类加载器,但是java层面获取不到,所以传null
          super(getExtURLs(extJarFiles), (ClassLoader)null, Launcher.factory);
          SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
    }

获取应用程序类加载器

  //获取应用类加载器,这里classerLoader是上一步获取的扩展类加载器
  public static ClassLoader getAppClassLoader(final ClassLoader classLoader) throws IOException {
        //获取程序的类路径定义信息,类似classpath的地址
            final String classPath = System.getProperty("java.class.path");
            final File[] var2 = classPath == null ? new File[0] : Launcher.getClassPath(classPath);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<AppClassLoader>() {
                public AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                    return new AppClassLoader(var1x, classLoader);
                }
            });
  }



  AppClassLoader(URL[] var1, ClassLoader classLoader) {
        /初始化父类URLClassLoader的相关逻辑,
          //其中第二个参数为指定父加载器,这里将扩展类加载器指定为应用程序类加载器的父加载器(不是父类,他们是平级的)
            super(var1, classLoader, Launcher.factory);
            this.ucp.initLookupCache(this);
  }

各类加载器加载的类地址

  public static void main(String[] args) {
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        ClassLoader extClassLoader = appClassLoader.getParent();
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();

        System.out.println(appClassLoader);
        System.out.println(extClassLoader);
        System.out.println(bootstrapClassLoader);

        System.out.println();

        System.out.println("bootstrapClassLoader加载路径:");
        bootstrapClassLoader加载路径:
        //file:/C:/application/java/jre/lib/resources.jar
        //file:/C:/application/java/jre/lib/rt.jar
        //file:/C:/application/java/jre/lib/sunrsasign.jar
        //file:/C:/application/java/jre/lib/jsse.jar
        //file:/C:/application/java/jre/lib/jce.jar
        //file:/C:/application/java/jre/lib/charsets.jar
        //file:/C:/application/java/jre/lib/jfr.jar
        //file:/C:/application/java/jre/classes
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (URL urL : urLs) {
            System.out.println(urL);
        }

        System.out.println();
        System.out.println("extClassLoader加载路径:");
        //C:\application\java\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
        System.out.println(System.getProperty("java.ext.dirs"));


        System.out.println();
        System.out.println("appClassLoader加载路径:");
        //这里也会打印出ext和bootstrap的类路径地址,但appClassLoader不会去加载这些类。因为双亲委派机制
        //appClassLoader主要加载target目录下的类
        System.out.println(System.getProperty("java.class.path"));

    }
双亲委派机制
JVM类加载器的亲子层结构图




加载流程:


1.当需要加载Some.class时,首先由appClassLoader查找自身有没有加载过该类,如果有,则直接返回,如果没有,则委托上一级加载

2.extClassLoader同样查找自身有没有加载过该类,如果有,则直接返回,如果没有,则委托上一级加载

3.bootstrapClassLoader同样查找自身有没有加载过该类,如果有,则直接返回,如果没有,则试着去加载。如果在自己类库中找不到该类,则加载不成功,将会委托下一级classLoader去加载该类。


4.extClassLoader尝试加载该类,如果在自己类库中找不到该类,则加载不成功,将会委托下一级classLoader去加载该类。

5.appClassLoader在自己目录下,也就是target目录下查找该类,找到,并加载执行该类。


双亲委派机制简单点就是,先找父加载器加载,不行再由子加载器加载


源码


当需要加载一个自定义Some.class时,从应用程序类加载器开始


ClassLoader关于类加载的方法,所有的类加载器均继承了该方法


1.查找当前加载器是否加载过该类(findLoadedClass)


  //查找当前类加载器是否加载过该类,如果有,直接返回,如果没有,返回null
  protected final Class<?> findLoadedClass(String name) {
        if (!checkName(name))
            return null;
        return findLoadedClass0(name);
    }

  //C++方法
  private native final Class<?> findLoadedClass0(String name);
  1. 查找引导类加载器是否加载过该类(findBootstrapClassOrNull)
private Class<?> findBootstrapClassOrNull(String name){
        if (!checkName(name)) return null;

        return findBootstrapClass(name);
}

//C++方法,查找引导类加载器是否加载过该类
private native Class<?> findBootstrapClass(String name);
  1. findClass()方法


从URL搜索路径中通过指定的类名称查找并加载类。


加载步骤:


  1. appClassLoader
  //appClasssLoader的loadClass方法
  public Class<?> loadClass(String var1, boolean resolve) throws ClassNotFoundException {
            int var3 = var1.lastIndexOf(46);
            if (var3 != -1) {
                SecurityManager var4 = System.getSecurityManager();
                if (var4 != null) {
                    var4.checkPackageAccess(var1.substring(0, var3));
                }
            }

            if (this.ucp.knownToNotExist(var1)) {
                //appClassLoader尝试着从自己已经加载过的类中查找该类。
                Class var5 = this.findLoadedClass(var1);
                if (var5 != null) {
                    if (resolve) {
                        this.resolveClass(var5);
                    }

                    return var5;
                } else {
                    throw new ClassNotFoundException(var1);
                }
            } else {
                //调用URLClassLoader加载loadClass,注意这里super不是extClassLoader,这个是继承关系,找的是父类方法。
                //extClassLoader是appClassLoader的父加载器
                return super.loadClass(var1, resolve);
            }
      }


  1. urlClassLoader
public final Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        // First check if we have permission to access the package. This
        // should go away once we've added support for exported packages.
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageAccess(name.substring(0, i));
            }
    }
      //classLoader.loadClass();
        return super.loadClass(name, resolve);
}
  1. ClassLoader


protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            //第一步,从已经加载过的class中找,注意这里的this对象是appClassLoader
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //此时parent是extClassLoader
                    if (parent != null) {
                        //向extClassLoader委托加载类
                        c = parent.loadClass(name, false);
                    } else {
                        //extClassLoader的parent为bootstrapClassLoader,但是java获取不到,所以为null,
                        //此时查找引导类加载器是否加载过该类
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //extClassLoader尝试加载该类,如果没有加载,则c为null,交由下一级类加载器,也就是appClassLoader尝试加载
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }


为什么要设计双亲委派机制


1.沙箱安全机制: 自己写的java.lang.String.class类不会被加载,这样防止核心api库被随意篡改(核心类库永远都是bootstrap加载,所以appClassLoader无法加载和核心类库全路径一样的类)


2.避免类的重复加载:当父加载器已经加载了该类,就没有必要子类加载器再加载一次,保证被加载的类的唯一性


全盘委托机制


全盘负责是指当一个ClassLoader装在一个类时,除非显示的使用另外一个ClassLoader,该类所依赖及引用的类也由这个ClassLoader载入。


自定义类加载器


自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是loadClass(Sting,boolean),实现了双亲委派机制,还有一个

方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findclass方法。


自定义类加载的器的父加载器默认为appClassLoader,这样默认满足双亲委派机制。


打破双亲委派机制


以Tomcat类加载为例,Tomcat 如果使用默认的双亲委派类加载机制行不行?


我们思考一下:Tomcat是个web容器,那么它要解决什么问题:


1.一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类都是独立,保证相互隔离。


2.部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加进虚拟机。


3.web容器也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。


4.web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已,经是司空见惯的事情,web容器要支持jsp的修改后不用重启。



目录
相关文章
|
7天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
23 2
|
11天前
|
Java 编译器
探索Java中的异常处理机制
【10月更文挑战第35天】在Java的世界中,异常是程序运行过程中不可避免的一部分。本文将通过通俗易懂的语言和生动的比喻,带你了解Java中的异常处理机制,包括异常的类型、如何捕获和处理异常,以及如何在代码中有效地利用异常处理来提升程序的健壮性。让我们一起走进Java的异常世界,学习如何优雅地面对和解决问题吧!
|
1月前
|
存储 算法 Java
Java HashSet:底层工作原理与实现机制
本文介绍了Java中HashSet的工作原理,包括其基于HashMap实现的底层机制。通过示例代码展示了HashSet如何添加元素,并解析了add方法的具体过程,包括计算hash值、处理碰撞及扩容机制。
|
22天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
35 5
Java反射机制:解锁代码的无限可能
|
10天前
|
Java 数据库连接 开发者
Java中的异常处理机制及其最佳实践####
在本文中,我们将探讨Java编程语言中的异常处理机制。通过深入分析try-catch语句、throws关键字以及自定义异常的创建与使用,我们旨在揭示如何有效地管理和响应程序运行中的错误和异常情况。此外,本文还将讨论一些最佳实践,以帮助开发者编写更加健壮和易于维护的代码。 ####
|
16天前
|
安全 IDE Java
Java反射Reflect机制详解
Java反射(Reflection)机制是Java语言的重要特性之一,允许程序在运行时动态地获取类的信息,并对类进行操作,如创建实例、调用方法、访问字段等。反射机制极大地提高了Java程序的灵活性和动态性,但也带来了性能和安全方面的挑战。本文将详细介绍Java反射机制的基本概念、常用操作、应用场景以及其优缺点。 ## 基本概念 ### 什么是反射 反射是一种在程序运行时动态获取类的信息,并对类进行操作的机制。通过反射,程序可以在运行时获得类的字段、方法、构造函数等信息,并可以动态调用方法、创建实例和访问字段。 ### 反射的核心类 Java反射机制主要由以下几个类和接口组成,这些类
36 2
|
21天前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
23 3
|
21天前
|
安全 Java UED
深入理解Java中的异常处理机制
【10月更文挑战第25天】在编程世界中,错误和意外是不可避免的。Java作为一种广泛使用的编程语言,其异常处理机制是确保程序健壮性和可靠性的关键。本文通过浅显易懂的语言和实际示例,引导读者了解Java异常处理的基本概念、分类以及如何有效地使用try-catch-finally语句来处理异常情况。我们将从一个简单的例子开始,逐步深入到异常处理的最佳实践,旨在帮助初学者和有经验的开发者更好地掌握这一重要技能。
20 2
|
23天前
|
Java 数据库连接 开发者
Java中的异常处理机制####
本文深入探讨了Java语言中异常处理的核心概念,通过实例解析了try-catch语句的工作原理,并讨论了finally块和throws关键字的使用场景。我们将了解如何在Java程序中有效地管理错误,提高代码的健壮性和可维护性。 ####
|
26天前
|
安全 Java 程序员
深入浅出Java中的异常处理机制
【10月更文挑战第20天】本文将带你一探Java的异常处理世界,通过浅显易懂的语言和生动的比喻,让你在轻松阅读中掌握Java异常处理的核心概念。我们将一起学习如何优雅地处理代码中不可预见的错误,确保程序的健壮性和稳定性。准备好了吗?让我们一起踏上这段旅程吧!
25 6