0.1 、Spring 源码学习-JVM类加载器-双亲委派模型-Spring 中类加载机制

文章目录

        • JVM 类加载机制
        • 双亲委派模型
          • 自定义类加载器的原则:组合而非继承
        • Spring 类加载机制
          • 入口 AbstractApplicationContext.prepareBeanFactory
          • DefaultResourceLoader.getClassLoader()
          • ClassUtils.getDefaultClassLoader()
        • 延伸阅读:破坏双亲委派模型

JVM 类加载机制

/**
* 获取类加载器的测试方法
*/
@Test
public void test2() {
	//获取Test类的类加载器 sun.misc.Launcher$AppClassLoader@4dc63996
	ClassLoader c  = Test.class.getClassLoader();  
       System.out.println(c); 
       //获取c这个类加载器的父类加载器 sun.misc.Launcher$ExtClassLoader@28a418fc
       ClassLoader c1 = c.getParent(); 
       System.out.println(c1); 
       // getClassLoader() returning null indicates the bootstrap ClassLoader
       //获取c1这个类加载器的父类加载器 ,null,因为Bootstrp loader是C++编写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体
       //根装载器:Bootstrp loader
       //用C++语言写的,它是在Java虚拟机启动后初始化的,主要负责加载%JAVA_HOME%/jre/lib,
       //* -Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。
       ClassLoader c2 = c1.getParent();
       System.out.println(c2);
       
       //获取系统默认的ClassLoader sun.misc.Launcher$AppClassLoader@4dc63996
       ClassLoader c3=ClassLoader.getSystemClassLoader();
       System.out.println(c3);
       //获取创建当前线程的类加载器 
       ClassLoader c4= Thread.currentThread().getContextClassLoader();
       System.out.println(c4);
}

双亲委派模型

简单说就是,先由自己的父类加载需要的类,一直向上传递,如果没有再由自己加载
类加载器可以重写,但是核心类是不允许使用自己的类加载器加载的,比如 jdk 核心包 java.lang.String ,只允许 bootstrap ClassLoader 进行加载

  • BootStrap ClassLoader:称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,JAVA_HOME/JRE/lib/,如:rt.jar、resources.jar、charsets.jar等
  • Extension ClassLoader:称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/JRE/lib/ext/目下的所有jar
  • App ClassLoader:称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件
自定义类加载器的原则:组合而非继承

这里类加载器之间的父子关系一般不会以继承的关系来实现,而是都使用组合关系来复用父加载器的代码。
这意味着,当你在自定义类加载器时,也应该先委托父加载器即App ClassLoader进行加载。
那么问题来了,既然双亲委派模型只是一个规范,那我可以直接使用自定义加载器加载 String 类吗?答案是:不可以。因为JVM会检测,即使你不遵守双亲委派模型,你也无法加载核心类库的类,JVM自己会检测。

Spring 类加载机制

入口 AbstractApplicationContext.prepareBeanFactory
beanFactory.setBeanClassLoader(getClassLoader());
DefaultResourceLoader.getClassLoader()

org.springframework.core.io.DefaultResourceLoader

/**
* Return the ClassLoader to load class path resources with.
 * 

Will get passed to ClassPathResource's constructor for all * ClassPathResource objects created by this resource loader. * @see ClassPathResource */ @Override public ClassLoader getClassLoader() { //如果指定了类加载器就返回指定的类加载器,否则获取线程类加载器->不存在就获取ClassUtils类加载器-> 不存在就获取系统默认类加载器 return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader()); }

ClassUtils.getDefaultClassLoader()

org.springframework.util.ClassUtils.getDefaultClassLoader()
看中文注释

/**
* Return the default ClassLoader to use: typically the thread context
 * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
 * class will be used as fallback.
 * 

Call this method if you intend to use the thread context ClassLoader * in a scenario where you clearly prefer a non-null ClassLoader reference: * for example, for class path resource loading (but not necessarily for * {@code Class.forName}, which accepts a {@code null} ClassLoader * reference as well). * @return the default ClassLoader (only {@code null} if even the system * ClassLoader isn't accessible) * @see Thread#getContextClassLoader() * @see ClassLoader#getSystemClassLoader() */ public static ClassLoader getDefaultClassLoader() { ClassLoader cl = null; try { //获取当前线程类加载器 cl = Thread.currentThread().getContextClassLoader(); } catch (Throwable ex) { // Cannot access thread context ClassLoader - falling back... } //如果线程类加载器为null if (cl == null) { // No thread context class loader -> use class loader of this class. //获取当前类的类加载器 cl = ClassUtils.class.getClassLoader(); if (cl == null) { // getClassLoader() returning null indicates the bootstrap ClassLoader //getClassLoader() 为 null 说明是 bootstrap ClassLoader 类加载器,该加载器为 C++ 编写,故从Java 角度看是 null try { //获取当前系统的类加载器 cl = ClassLoader.getSystemClassLoader(); } catch (Throwable ex) { // Cannot access system ClassLoader - oh well, maybe the caller can live with null... } } } return cl; }

延伸阅读:破坏双亲委派模型

Tomcat 类加载器之为何违背双亲委派模型

你可能感兴趣的:(#,Spring,源码学习)