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的修改后不用重启。



目录
相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2
|
1月前
|
Java 编译器
探索Java中的异常处理机制
【10月更文挑战第35天】在Java的世界中,异常是程序运行过程中不可避免的一部分。本文将通过通俗易懂的语言和生动的比喻,带你了解Java中的异常处理机制,包括异常的类型、如何捕获和处理异常,以及如何在代码中有效地利用异常处理来提升程序的健壮性。让我们一起走进Java的异常世界,学习如何优雅地面对和解决问题吧!
|
21天前
|
Java 程序员
深入理解Java异常处理机制
Java的异常处理是编程中的一块基石,它不仅保障了代码的健壮性,还提升了程序的可读性和可维护性。本文将深入浅出地探讨Java异常处理的核心概念、分类、处理策略以及最佳实践,旨在帮助读者建立正确的异常处理观念,提升编程效率和质量。
102 1
|
22天前
|
Java 开发者 UED
深入探索Java中的异常处理机制##
本文将带你深入了解Java语言中的异常处理机制,包括异常的分类、异常的捕获与处理、自定义异常的创建以及最佳实践。通过具体实例和代码演示,帮助你更好地理解和运用Java中的异常处理,提高程序的健壮性和可维护性。 ##
46 2
|
22天前
|
Java 开发者
Java中的异常处理机制深度剖析####
本文深入探讨了Java语言中异常处理的重要性、核心机制及其在实际编程中的应用策略,旨在帮助开发者更有效地编写健壮的代码。通过实例分析,揭示了try-catch-finally结构的最佳实践,以及如何利用自定义异常提升程序的可读性和维护性。此外,还简要介绍了Java 7引入的多异常捕获特性,为读者提供了一个全面而实用的异常处理指南。 ####
46 2
|
25天前
|
Java 程序员 UED
深入理解Java中的异常处理机制
本文旨在揭示Java异常处理的奥秘,从基础概念到高级应用,逐步引导读者掌握如何优雅地管理程序中的错误。我们将探讨异常类型、捕获流程,以及如何在代码中有效利用try-catch语句。通过实例分析,我们将展示异常处理在提升代码质量方面的关键作用。
33 3
|
25天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
26天前
|
运维 Java 编译器
Java 异常处理:机制、策略与最佳实践
Java异常处理是确保程序稳定运行的关键。本文介绍Java异常处理的机制,包括异常类层次结构、try-catch-finally语句的使用,并探讨常见策略及最佳实践,帮助开发者有效管理错误和异常情况。
80 4
|
25天前
|
开发框架 安全 Java
Java 反射机制:动态编程的强大利器
Java反射机制允许程序在运行时检查类、接口、字段和方法的信息,并能操作对象。它提供了一种动态编程的方式,使得代码更加灵活,能够适应未知的或变化的需求,是开发框架和库的重要工具。
40 2
|
29天前
|
Java
深入探讨Java中的中断机制:INTERRUPTED和ISINTERRUPTED方法详解
在Java多线程编程中,中断机制是协调线程行为的重要手段。了解和正确使用中断机制对于编写高效、可靠的并发程序至关重要。本文将深入探讨Java中的`Thread.interrupted()`和`Thread.isInterrupted()`方法的区别及其应用场景。
30 4