【ClassLoader】阅读过本文之后,反手在简历上写下:熟悉Java的类加载机制,不服来问!

 
愿你如阳光,明媚不忧伤。

 


1. ClassLoader 类加载器

ClassLoader 用来加载Java类到 Java 虚拟机中的一种加载器。与普通程序不同的是。Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里运行,负责加载Java class的这部分就叫做Class Loader。

  • BootstrapClassLoader(启动类加载器)
    c++编写,是虚拟机的内置类加载器,通常表示为null ,并且没有父级。负责加载java核心库 java.*,构造PlatformClassLoader和AppClassLoader。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
  • PlatformClassLoader (平台类加载器,jdk8以下版本为ExtClassLoader)
    java编写,加载java扩展库 javax.*,开发者可以直接使用平台类加载器。
  • SystemClassLoader / AppClassLoader(系统类 / 应用程序加载器)
    java编写,也称为应用程序类加载器,加载程序所在的目录模块路径和JDK特定工具上的定义类,所有在 System.getProperty("java.class.path")目录下的类都可以被这个类加载器加载,这个目录就是我们经常用到classpath
  • CustomClassLoader(用户自定义类加载器)
    JVM自带的ClassLoader只从本地文件系统加载标准的java class文件,如果想要做到以下需求,就需要用户自定义的类加载器。
    1.在执行非置信代码之前,自动验证数字签名;
    2.动态地创建符合用户特定需要的定制化构建类;
    3.从特定的场所取得java class,例如数据库中。
    当你决定创建你自己的ClassLoader时,需要继承java.lang.ClassLoader或者它的子类。在实例化每个ClassLoader对象时,需要指定一个父对象;如果没有指定的话,系统自动指定ClassLoader.getSystemClassLoader()为父对象。
    所以当创建自己的ClassLoader时,只需要重载findClass()这个方法。
package com.it.god.controller;

import java.util.Iterator;

import com.it.god.entity.Person;

public class RefectionCollection {

    public static void main(String[] args) throws ClassNotFoundException {

        // 第一种方式 通过Class类的静态方法——forName()来实现
        Class<?> class1 = Class.forName("com.it.god.entity.Person");
        // 第二种方式 通过类的class属性
        Class<?> class2 = Person.class;
        // 第三种方式 通过对象getClass方法
        Person person = new Person();
        Class<?> class3 = person.getClass();

        ClassLoader cl = class1.getClassLoader();

        System.out.println("ClassLoader is:" + cl.toString());
        System.out.println("ClassLoader\'s parent is:" + cl.getParent().toString());
        System.out.println(System.getProperty("java.class.path"));
        Iterator<Object> it = System.getProperties().keySet().iterator();
        while (it.hasNext()) {
            String str = (String) it.next();
            System.out.println(str);
        }
    }

}
-----------------------------------------------------------------
・【CONSOLE】实行结果
	ClassLoader is:jdk.internal.loader.ClassLoaders$AppClassLoader@69663380
	ClassLoader's parent is:jdk.internal.loader.ClassLoaders$PlatformClassLoader@5aaa6d82
	D:\ITGodRoad\Hiber\target\classes;D:\apache\apache-tomcat-9.0.39\lib\annotations-api.jar;D:\apache\apache-tomcat-9.0.39\lib\catalina-ant.jar;D:\apache\apache-tomcat-9.0.39\lib\catalina-ha.jar;D:\apache\apache-tomcat-9.0.39\lib\catalina-ssi.jar;D:\apache\apache-tomcat-9.0.39\lib\catalina-storeconfig.jar;D:\apache\apache-tomcat-9.0.39\lib\catalina-tribes.jar;D:\apache\apache-tomcat-9.0.39\lib\catalina.jar;D:\apache\apache-tomcat-9.0.39\lib\ecj-4.15.jar;D:\apache\apache-tomcat-9.0.39\lib\el-api.jar;D:\apache\apache-tomcat-9.0.39\lib\jasper-el.jar;D:\apache\apache-tomcat-9.0.39\lib\jasper.jar;D:\apache\apache-tomcat-9.0.39\lib\jaspic-api.jar;D:\apache\apache-tomcat-9.0.39\lib\jsp-api.jar;D:\apache\apache-tomcat-9.0.39\lib\servlet-api.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-api.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-coyote.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-dbcp.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-cs.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-de.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-es.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-fr.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-ja.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-ko.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-pt-BR.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-ru.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-i18n-zh-CN.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-jdbc.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-jni.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-util-scan.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-util.jar;D:\apache\apache-tomcat-9.0.39\lib\tomcat-websocket.jar;D:\apache\apache-tomcat-9.0.39\lib\websocket-api.jar;D:\mavenRepository\org\springframework\spring-core\5.3.1\spring-core-5.3.1.jar;D:\mavenRepository\org\springframework\spring-jcl\5.3.1\spring-jcl-5.3.1.jar;D:\mavenRepository\org\springframework\spring-webmvc\5.3.1\spring-webmvc-5.3.1.jar;D:\mavenRepository\org\springframework\spring-aop\5.3.1\spring-aop-5.3.1.jar;D:\mavenRepository\org\springframework\spring-beans\5.3.1\spring-beans-5.3.1.jar;D:\mavenRepository\org\springframework\spring-context\5.3.1\spring-context-5.3.1.jar;D:\mavenRepository\org\springframework\spring-expression\5.3.1\spring-expression-5.3.1.jar;D:\mavenRepository\org\springframework\spring-web\5.3.1\spring-web-5.3.1.jar;D:\mavenRepository\org\springframework\spring-jdbc\5.3.1\spring-jdbc-5.3.1.jar;D:\mavenRepository\org\springframework\spring-tx\5.3.1\spring-tx-5.3.1.jar;D:\mavenRepository\org\hibernate\hibernate-core\5.4.26.Final\hibernate-core-5.4.26.Final.jar;D:\mavenRepository\org\jboss\logging\jboss-logging\3.4.1.Final\jboss-logging-3.4.1.Final.jar;D:\mavenRepository\javax\persistence\javax.persistence-api\2.2\javax.persistence-api-2.2.jar;D:\mavenRepository\org\javassist\javassist\3.27.0-GA\javassist-3.27.0-GA.jar;D:\mavenRepository\net\bytebuddy\byte-buddy\1.10.17\byte-buddy-1.10.17.jar;D:\mavenRepository\antlr\antlr\2.7.7\antlr-2.7.7.jar;D:\mavenRepository\org\jboss\spec\javax\transaction\jboss-transaction-api_1.2_spec\1.1.1.Final\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;D:\mavenRepository\org\jboss\jandex\2.1.3.Final\jandex-2.1.3.Final.jar;D:\mavenRepository\com\fasterxml\classmate\1.5.1\classmate-1.5.1.jar;D:\mavenRepository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;D:\mavenRepository\org\dom4j\dom4j\2.1.3\dom4j-2.1.3.jar;D:\mavenRepository\org\hibernate\common\hibernate-commons-annotations\5.1.2.Final\hibernate-commons-annotations-5.1.2.Final.jar;D:\mavenRepository\javax\xml\bind\jaxb-api\2.3.1\jaxb-api-2.3.1.jar;D:\mavenRepository\org\glassfish\jaxb\jaxb-runtime\2.3.1\jaxb-runtime-2.3.1.jar;D:\mavenRepository\org\glassfish\jaxb\txw2\2.3.1\txw2-2.3.1.jar;D:\mavenRepository\com\sun\istack\istack-commons-runtime\3.0.7\istack-commons-runtime-3.0.7.jar;D:\mavenRepository\org\jvnet\staxex\stax-ex\1.8\stax-ex-1.8.jar;D:\mavenRepository\com\sun\xml\fastinfoset\FastInfoset\1.2.15\FastInfoset-1.2.15.jar;D:\mavenRepository\org\springframework\spring-orm\4.2.4.RELEASE\spring-orm-4.2.4.RELEASE.jar;D:\mavenRepository\mysql\mysql-connector-java\8.0.22\mysql-connector-java-8.0.22.jar;D:\mavenRepository\com\google\protobuf\protobuf-java\3.11.4\protobuf-java-3.11.4.jar;D:\mavenRepository\com\alibaba\fastjson\1.2.7\fastjson-1.2.7.jar;D:\mavenRepository\com\weicoder\redis\3.4.6\redis-3.4.6.jar;D:\mavenRepository\com\weicoder\common\3.4.6\common-3.4.6.jar;D:\mavenRepository\com\weicoder\cache\3.4.6\cache-3.4.6.jar;D:\mavenRepository\com\google\guava\guava\30.1-jre\guava-30.1-jre.jar;D:\mavenRepository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;D:\mavenRepository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;D:\mavenRepository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;D:\mavenRepository\org\checkerframework\checker-qual\3.5.0\checker-qual-3.5.0.jar;D:\mavenRepository\com\google\errorprone\error_prone_annotations\2.3.4\error_prone_annotations-2.3.4.jar;D:\mavenRepository\com\google\j2objc\j2objc-annotations\1.3\j2objc-annotations-1.3.jar;D:\mavenRepository\com\weicoder\json\3.4.6\json-3.4.6.jar;D:\mavenRepository\redis\clients\jedis\3.4.0\jedis-3.4.0.jar;D:\mavenRepository\org\apache\commons\commons-pool2\2.6.2\commons-pool2-2.6.2.jar;D:\mavenRepository\org\slf4j\slf4j-simple\1.7.30\slf4j-simple-1.7.30.jar;D:\mavenRepository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar
	sun.desktop
	awt.toolkit
	java.specification.version
	sun.cpu.isalist
	sun.jnu.encoding
	java.class.path
	java.vm.vendor
	sun.arch.data.model
	user.variant
	java.vendor.url
	user.timezone
	os.name
	java.vm.specification.version
	sun.java.launcher
	user.country
	sun.boot.library.path
	sun.java.command
	jdk.debug
	sun.cpu.endian
	user.home
	user.language
	java.specification.vendor
	java.version.date
	java.home
	file.separator
	java.vm.compressedOopsMode
	line.separator
	java.specification.name
	java.vm.specification.vendor
	java.awt.graphicsenv
	user.script
	sun.management.compiler
	java.runtime.version
	user.name
	path.separator
	os.version
	java.runtime.name
	file.encoding
	java.vm.name
	java.vendor.version
	java.vendor.url.bug
	java.io.tmpdir
	java.version
	user.dir
	os.arch
	java.vm.specification.name
	java.awt.printerjob
	sun.os.patch.level
	java.library.path
	java.vendor
	java.vm.info
	java.vm.version
	sun.io.unicode.encoding
	java.class.version

 

  • loadClass源码
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先检查这个class是否已经被初始化加载过了,没有则返回null
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                	// 如果父类的加载器不为空,则让父类加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                    	//如果父类的加载器为空 则递归到bootStrapClassloader加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // 如果在非空父类加载器中找不到类,则抛出ClassNotFoundException
                }
				// 如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class
                if (c == null) {
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // 定义类加载器;记录数据
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

 


2. 双亲委派(溯源委派)

parents delegate 双亲委派的原文是"parents delegate"。parents在英文中是“父母”、“双亲”的意思,但其实表达的是“父母这一辈”的人的意思。实际上这个模型中,只是表达“父母这一辈”的classloader而已,并不是说真的有一个父亲的classloader和一个母亲classloader。
当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。注意:双亲委派中的父子关系大多数场景下不见得是父子的继承关系,而是组合的关系。

 

  • 双亲委派模型

在这里插入图片描述

  • 优点
  1. 可以审查每个类该由谁加载(父类优先的等级加载机制);
  2. 保证数据安全。通过委托去向上面问一问,加载过了,就不用再加载一遍,防止重复加载同一个.class。;
  3. 保证核心.class不被篡改。通过委托方式不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象;
  4. 保证Class执行安全。不同的加载器加载同一个.class也不是同一个Class对象。
  • 如何破坏双亲委派
     
    知道了双亲委派模型的实现,那么想要破坏双亲委派机制就很简单了。因为他的双亲委派过程都是在loadClass方法中实现的,那么想要破坏这种机制,那么就自定义一个类加载器,重写其中的loadClass等方法,使其不进行双亲委派即可。

 


3. 自定义类加载器

 
本文中第一节提到过,想要自定义类加载器需要继承java.lang.ClassLoader或者它的子类。然后重写其中的方法:

  • 了解源码
  1. loadClass() 主要进行类加载的方法,默认的双亲委派机制就实现在这个方法中。
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
  1. findClass() 根据名称或位置加载.class字节码,在执行请求一个类时会先检查父加载器,然后将执行loadClass方法。
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
  1. defineclass() 把字节码转化为Class
    protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass1(this, name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }
  1. ClassLoader() 默认情况下创建ClassLoader时会调用其他构造器。且传入的parent为系统类加载器。
    private ClassLoader(Void unused, String name, ClassLoader parent) {
        this.name = name;
        this.parent = parent;
        this.unnamedModule = new Module(this);
        if (ParallelLoaders.isRegistered(this.getClass())) {
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            assertionLock = new Object();
        } else {
            // no finer-grained lock; lock on the classloader instance
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            assertionLock = this;
        }
        this.nameAndId = nameAndId(this);
    }
  • 自定义ClassLoader

    可以看到同一个Apple.class在不同的命名空间中,选择了不同的加载器,总共加载了三次,
package com.it.god.controller;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;

/**
 * 编写自定义类加载器: 1:继承ClassLoader 2:重写findClass 3:通过自定义的loadClassData,将字节码文件读取到字节数组中 4:通过defineClass方法将字节数组中的数据转换为Class对象 5:测试
 */
public class CustomClassLoader extends ClassLoader {
    // 定义加载的目录:
    private String path = "";
    // 定义加载文件的后缀
    private final static String fileType = ".class";
    // 定义类加载器的名称
    private String name;

    public CustomClassLoader(String name) {
        super();
        this.name = name;
    }

    public CustomClassLoader(String name, ClassLoader parent) {
        super(parent);
        this.name = name;
    }

    // 重写findClass
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = loadClassData(name);
        final Class<?> aClass = defineClass(name, data, 0, data.length);
        return aClass;
    }

    // 定义loadClass方法 通过全限定名称 获取字节数组
    private byte[] loadClassData(String name) {
        // 声明返回数据
        byte[] data = null;
        try (InputStream is = new FileInputStream(new File(path + name + fileType)); ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int len = 0;
            while ((len = is.read()) != -1) {
                baos.write(len);
            }
            data = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

    public void setPath(String path) {
        this.path = path;
    }

    @Override
    public String toString() {
        return "CustomClassLoader{" + "path='" + path + '\'' + ", name='" + name + '\'' + '}';
    }

    public static void main(String[] args) {
        CustomClassLoader customClassLoader01 = new CustomClassLoader("CustomClassLoader01");
        customClassLoader01.setPath("D:/CustomClass/CustomClassAnother01/");
        CustomClassLoader customClassLoader02 = new CustomClassLoader("CustomClassLoader02", customClassLoader01);
        customClassLoader02.setPath("D:/CustomClass/CustomClassAnother02");
        CustomClassLoader customClassLoader03 = new CustomClassLoader("CustomClassLoader03", null);
        customClassLoader03.setPath("D:/CustomClass/");
        loader(customClassLoader01);
        loader(customClassLoader02);
        loader(customClassLoader03);
        System.out.println(customClassLoader01.toString());
        System.out.println(customClassLoader02.toString());
        System.out.println(customClassLoader03.toString());
    }

    private static void loader(ClassLoader classLoader) {
        try {
            Class clz = classLoader.loadClass("Apple");
            Constructor c = clz.getConstructor(null);
            c.newInstance(null);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
-----------------------------------------------------------------
・【CONSOLE】实行结果
	CustomClassLoader{path='D:/CustomClass/CustomClassAnother01/', name='CustomClassLoader01'}
	CustomClassLoader{path='D:/CustomClass/CustomClassAnother02', name='CustomClassLoader02'}
	CustomClassLoader{path='D:/CustomClass/', name='CustomClassLoader03'}

 


4. ClassLoader.class 分析

class ClassLoader 类加载器是一个负责加载类的对象。 ClassLoader类是一个抽象类。 给定类的binary name ,类加载器应该尝试定位或生成构成类定义的数据。 典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。

  • 获取信息
返回值方法描述
Class<?>loadClass(String name)使用指定的二进制名加载类
static ClassLoadergetPlatformClassLoader()返回平台类加载器 (原ExtClassLoader)
static ClassLoadergetSystemClassLoader()返回系统类加载器(原AppClassLoader)
PackagegetDefinedPackage(String name)返回此类加载器定义的指定名软件包
Package[]getDefinedPackages()返回此类加载器定义的所有软件包数组
StringgetName()返回类加载器的名称
ClassLoadergetParent()返回委托的父类加载器
ModulegetUnnamedModule()返回此类加载器的未命名模块
booleanisRegisteredAsParallelCapable()判断类加载器是否为并行的加载器
  • 实例验证

    1.AppClassLoader的默认名称为app
    2.AppClassLoader的上级加载器是PlatFormClassLoader;
    3.AppClassLoader和PlatFormClassLoader都是并行的;
package com.it.god.controller;

import com.it.god.entity.Person;

public class ClassLoaderCollection {

    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Person();
        person.getClass().getClassLoader();
        
        System.out.println("ClassLoader.getPlatformClassLoader():\t\t" + ClassLoader.getPlatformClassLoader());
        System.out.println("person.getClass().getPlatformClassLoader():\t" + person.getClass().getClassLoader().getPlatformClassLoader());
        System.out.println("ClassLoader.getSystemClassLoader():\t\t" + ClassLoader.getSystemClassLoader());
        System.out.println("person.getClass().getSystemClassLoader():\t" + person.getClass().getClassLoader().getSystemClassLoader());
        System.out.println("getDefinedPackage(''):\t\t" + person.getClass().getClassLoader().getDefinedPackage(""));
        System.out.println("getDefinedPackages():\t\t" + person.getClass().getClassLoader().getDefinedPackages());
        System.out.println("getName():\t\t\t" + person.getClass().getClassLoader().getName());
        System.out.println("getParent():\t\t\t" + person.getClass().getClassLoader().getParent());
        System.out.println("getUnnamedModule()  :\t\t" + person.getClass().getClassLoader().getUnnamedModule());
        System.out.println("isRegisteredAsParallelCapable() :\t" + person.getClass().getClassLoader().isRegisteredAsParallelCapable());
        System.out.println("isRegisteredAsParallelCapable() :\t" + person.getClass().getClassLoader().getParent().isRegisteredAsParallelCapable());
    }
}
-----------------------------------------------------------------
・【CONSOLE】实行结果
	ClassLoader.getPlatformClassLoader():		jdk.internal.loader.ClassLoaders$PlatformClassLoader@5aaa6d82
	person.getClass().getPlatformClassLoader():	jdk.internal.loader.ClassLoaders$PlatformClassLoader@5aaa6d82
	ClassLoader.getSystemClassLoader():			jdk.internal.loader.ClassLoaders$AppClassLoader@69663380
	person.getClass().getSystemClassLoader():	jdk.internal.loader.ClassLoaders$AppClassLoader@69663380
	getDefinedPackage(''):		null
	getDefinedPackages():		[Ljava.lang.Package;@44e81672
	getName():					app
	getParent():				jdk.internal.loader.ClassLoaders$PlatformClassLoader@5aaa6d82
	getUnnamedModule()  :		unnamed module @60215eee
	isRegisteredAsParallelCapable() :	true
	isRegisteredAsParallelCapable() :	true

  • 断言状态

    包默认值优先于类加载器的默认断言状态,可以通过调用setClassAssertionStatus(String, boolean)在每个类的基础上重写 。
返回值方法描述
voidsetClassAssertionStatus(String className, boolean enabled)是否在类初始化时启用断言(顶级类的完全限定类名)
voidsetDefaultAssertionStatus(boolean enabled)设置此类加载器的默认断言状态
voidsetPackageAssertionStatus(String packageName, boolean enabled)设置包的默认断言状态(packageName 为nul表示当前包)
voidclearAssertionStatus()清空加载器的断言状态
  • 实例验证
package com.it.god.controller;

import com.it.god.entity.Person;

public class ClassLoaderCollection {

    public static void main(String[] args) throws ClassNotFoundException {
        
        System.out.println("略);
    }
}
-----------------------------------------------------------------
・【CONSOLE】实行结果
	略   
  • 查找指定名称资源
返回值方法描述
Stream<URL>resources(String name)返回路径流
URLgetResource(String name)返回完整路径
Enumeration<URL>getResources(String name)返回枚举
InputStreamgetResourceAsStream(String name)返回输入流
static URLgetSystemResource(String name)调用上级加载器返回完整路径
static Enumeration<URL>getSystemResources(String name)调用上级加载器返回枚举
static InputStreamgetSystemResourceAsStream(String name)调用上级加载器返回输入流
  • 实例验证

    1.ClassLoader.getSystemResource("")等价于ClassLoader.getSystemClassLoader().getResource("");
    2.ClassLoader.getSystemClassLoader("")是用AppClassLoader加载的,而Class.getClassLoader("")是用URLClassLoader加载的;
    3.class.getResource("")不加/表示直接找到某个class的类路径搜索资源,而加/表示找到某个class的类路径的根目录开始搜索资源;
    4.classloader.getResource("")不加/表示直接找到工程根目录开始搜索资源,而加上/则会返回null
    5.classload.getResource("") 等价于 class.getResource("/"),
package com.it.god.controller;

import com.it.god.entity.Person;

public class ClassLoaderCollection {

    public static void main(String[] args) throws ClassNotFoundException {

        Person person = new Person();

        System.out.println("class.getResource(''):\t\t" + person.getClass().getResource(""));
        System.out.println("class.getResource('/'):\t\t" + person.getClass().getResource("/"));
        System.out.println("class.getResource('src'):\t" + person.getClass().getResource("/hibernate.properties"));
        System.out.println("classLoader.getResource(''):\t\t" + person.getClass().getClassLoader().getResource(""));
        System.out.println("classLoader.getResource('/'):\t\t" + person.getClass().getClassLoader().getResource("/"));
        System.out.println("classLoader.getResource('src'):\t\t" + person.getClass().getClassLoader().getResource("hibernate.properties"));
        System.out.println("classLoader.getSystemResource(''):\t\t" + ClassLoader.getSystemResource(""));
        System.out.println("classLoader.getSystemResource('/'):\t\t" + ClassLoader.getSystemResource(""));
        System.out.println("classLoader.getSystemResource('src'):\t\t" + ClassLoader.getSystemResource("hibernate.properties"));
        System.out.println("classLoader.getSystemClassLoader().getSystemResource(''):\t\t" + ClassLoader.getSystemClassLoader().getResource(""));
        System.out.println("classLoader.getSystemClassLoader().getSystemResource('/'):\t\t" + ClassLoader.getSystemClassLoader().getResource(""));
        System.out.println(
                "classLoader.getSystemClassLoader().getSystemResource('src'):\t\t" + ClassLoader.getSystemClassLoader().getResource("hibernate.properties"));          
		System.out.println("classLoader.getResource(''):\t\t" + person.getClass().getClassLoader().resources(""));
    }

}
-----------------------------------------------------------------
・【CONSOLE】实行结果
	class.getResource(''):		file:/D:/ITGodRoad/Hiber/target/classes/com/it/god/entity/
	class.getResource('/'):		file:/D:/ITGodRoad/Hiber/target/classes/
	class.getResource('src'):	file:/D:/ITGodRoad/Hiber/target/classes/hibernate.properties
	classLoader.getResource(''):		file:/D:/ITGodRoad/Hiber/target/classes/
	classLoader.getResource('/'):		null
	classLoader.getResource('src'):		file:/D:/ITGodRoad/Hiber/target/classes/hibernate.properties
	classLoader.getSystemResource(''):		file:/D:/ITGodRoad/Hiber/target/classes/
	classLoader.getSystemResource('/'):		file:/D:/ITGodRoad/Hiber/target/classes/
	classLoader.getSystemResource('src'):	file:/D:/ITGodRoad/Hiber/target/classes/hibernate.properties
	classLoader.getSystemClassLoader().getSystemResource(''):		file:/D:/ITGodRoad/Hiber/target/classes/
	classLoader.getSystemClassLoader().getSystemResource('/'):		file:/D:/ITGodRoad/Hiber/target/classes/
	classLoader.getSystemClassLoader().getSystemResource('src'):	file:/D:/ITGodRoad/Hiber/target/classes/hibernate.properties
	classLoader.getResource(''):		java.util.stream.ReferencePipeline$Head@366e2eef
	

 


【每日一面】

为什么Tomcat要破坏双亲委派

我们知道,Tomcat是web容器,那么一个web容器可能需要部署多个应用程序
不同的应用程序可能会依赖同一个第三方类库的不同版本,但是不同版本的类库中某一个类的全路径名可能是一样的。
如多个应用都要依赖ouseki.jar,但是A应用需要依赖1.0.0版本,但是B应用需要依赖1.0.1版本。这两个版本中都有一个类是com.ouseki.Test.class。
如果采用默认的双亲委派类加载机制,那么是无法加载多个相同的类。
所以,Tomcat破坏双亲委派原则,提供隔离的机制,为每个web容器单独提供一个WebAppClassLoader加载器
Tomcat的类加载机制:为了实现隔离性,优先加载 Web 应用自己定义的类,所以没有遵照双亲委派的约定,每一个应用自己的类加载器——WebAppClassLoader负责加载本身的目录下的class文件,加载不到时再交给CommonClassLoader加载,这和双亲委派刚好相反。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值