精华内容
下载资源
问答
  • 类的加载顺序
    2021-10-06 11:19:42


    前言

    在本地domain的一次更新jar包之后,本地的某个function直接挂了,经过检查,发现是是由于两个不同包的同名类,同名类的内部代码不同,导致代码校验类型出错。

    为了方便理解,我们先从类加载过程开始讲起

    一、类加载过程

    (原文 https://segmentfault.com/a/1190000023876273 )
    先在方法区找class信息,有的话直接调用,没有的话则使用类加载器加载到方法区(静态成员放在静态区,非静态成功放在非静态区),静态代码块在类加载时自动执行代码,非静态的不执行;先父类后子类,先静态后非静态;静态方法和非静态方法都是被动调用,即不调用就不执行。
    类加载的流程如下:

    1.加载

    • 通过类的全名限定获取定义此类的二进制字节流。
    • 将字节流代表的静态存储结构转化为方法区的运行时数据结构。
    • 在内存(方法区)生成一个代表这个类的class对象,作为方法区这个类的各种数据访问入口。

    加载和连接是交叉进行的,加载未完成可能连接已经开始了。

    2.验证

    ​ 检查class是否符合要求,非必须阶段,对程序的运行期没有影响,-Xverif:none 关闭(可以提高启动速度)

    • 文件格式验证(魔数、常量类型);
    • 元数据验证(语义分析);
    • 字节码验证(语义合法);
    • 符号引用验证;

    3.准备

    ​ 正式为类变量(static成员变量)分配内存并设置类变量初始值(零值)的阶段,这个变量所使用的内存都将在方法区进行分配,这时候的内存分配仅包括类变量,而不包括实例变量,实例变量将会在对象实例化时随着对象一起在堆中进行分配。

    4.解析

    ​ 虚拟机将常量池内的符号引用替换为直接引用的过程。

    5.初始化

    ​ 初始化阶段是执行类构造器<clinit>()方法的过程,虚拟机会保证一个类的类构造器()在多线程环境中被正确的加锁,同步;如果多个线程同时初始化一个类,那么只会有一个线程区执行这个类的类构造器,其他线程阻塞等待,直到<clinit>()方法完毕,同一个类加载器,一个类只会被初始化一次。


    二、什么是 ClassLoader

    (https://segmentfault.com/a/1190000008491597)
    当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不同的 class 文件当中,所以经常要从这个 class 文件中要调用另外一个 class 文件中的方法,如果另外一个文件不存在的,则会引发系统异常。而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个 class 文件到内存当中的,从而只有 class 文件被载入到了内存之后,才能被其它 class 所引用。所以 ClassLoader 就是用来动态加载 class 文件到内存当中用的。

    ClassLoader 作用:

    • 负责将 Class 加载到 JVM 中
    • 审查每个类由谁加载(父优先的等级加载机制)
    • 将 Class 字节码重新解析成 JVM 统一要求的对象格式

    1、ClassLoader 类结构分析

    class loader 是一个负责加载 classes 的对象,ClassLoader 类是一个抽象类,需要给出类的二进制名称,class loader 尝试定位或者产生一个 class 的数据,一个典型的策略是把二进制名字转换成文件名然后到文件系统中找到该文件。

    以下是 ClassLoader 常用到的几个方法及其重载方法:

    • ClassLoader
    • defineClass(byte[], int, int) 把字节数组 b中的内容转换成 Java 类,返回的结果是java.lang.Class类的实例。这个方法被声明为final的
    • findClass(String name) 查找名称为 name的类,返回的结果是java.lang.Class类的实例
    • loadClass(String name) 加载名称为 name的类,返回的结果是java.lang.Class类的实例
    • resolveClass(Class<?>) 链接指定的 Java 类

    2.ClassLoader 的等级加载机制

    Java默认提供的三个ClassLoader

    • BootStrap ClassLoader:称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等,可通过如下程序获得该类加载器从哪些地方加载了相关的jar或class文件
      代码如下(示例):
    public class BootStrapTest
    {
        public static void main(String[] args)
        {
          URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
          for (int i = 0; i < urls.length; i++) {
              System.out.println(urls[i].toExternalForm());
           }
        }
    }
    
    C:\Program Files\Java\jre1.8.0_151\lib\resources.jar;
    C:\Program Files\Java\jre1.8.0_151\lib\rt.jar;
    C:\Program Files\Java\jre1.8.0_151\lib\sunrsasign.jar;
    C:\Program Files\Java\jre1.8.0_151\lib\jsse.jar;
    C:\Program Files\Java\jre1.8.0_151\lib\jce.jar;
    C:\Program Files\Java\jre1.8.0_151\lib\charsets.jar;
    C:\Program Files\Java\jre1.8.0_151\lib\jfr.jar;
    C:\Program Files\Java\jre1.8.0_151\classes
    
    • Extension ClassLoader:称为扩展类加载器,负责加载Java的扩展类库,Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。默认加载JAVA_HOME/jre/lib/ext/目下的所有jar。
    • App ClassLoader:称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

    3.ClassLoader加载类的原理

    https://image-static.segmentfault.com/223/583/2235836364-58e8671a820d6_fix732


    三、同名类加载顺序

    https://blog.csdn.net/jerry741/article/details/108771914?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link

    https://modouxiansheng.top/2019/06/14/%E4%B8%8D%E5%AD%A6%E6%97%A0%E6%95%B0-%E5%B7%A5%E4%BD%9C%E5%8D%8A%E5%B9%B4%E9%81%87%E5%88%B0%E6%9C%80%E5%A5%87%E8%91%A9%E7%9A%84%E9%97%AE%E9%A2%98-2019/

    经过试验发现Java的获取文件夹下面的所有文件是跟操作系统的文件系统有关系的,相同的文件夹内容,在Windows中取出来,输出名字你会发现输出是经过a-z排序过的,但是在macOS或者Linux中你可以根据命令ll -fi就可以输出自然顺序,你会发现没有什么规律可言。

    Linux 系统文件存储默认顺序:
    取决于磁盘格式
    https://unix.stackexchange.com/questions/13451/what-is-the-directory-order-of-files-in-a-directory-used-by-ls-u#comment18027_13456
    https://zhuanlan.zhihu.com/p/105519189


    总结

    Q&A

    更多相关内容
  • 主要介绍了详解java中继承关系类加载顺序问题的相关资料,需要的朋友可以参考下
  • Java中加载顺序

    2021-01-21 17:09:58
    不过我们可以换个直接的问法,如果A和B中有静态变量,静态语句块,非静态变量,非静态语句块,构造函数,静态方法,非静态方法,同时A继承B,请问当实例化A时,内部的加载顺序是什么?  当时我也是一头...
  • 主要介绍了Java中加载顺序剖析(常用于面试题),本文直接给出代码实例和运行结果,给后给出了加载过程总结,需要的朋友可以参考下
  • 类加载顺序

    千次阅读 2018-09-27 20:27:50
    二、类加载顺序 三、一个具体说明的例子 四、类加载前和加载后的执行顺序(转自http://blog.csdn.net/mrzhoug/article/details/51581994) 一、类加载做了哪些事?  之前没有进行类加载  1.类加载,同时初始...

    目录

    一、类加载做了哪些事?

    二、类加载的顺序

    三、一个具体说明的例子

    四、类加载前和加载后的执行顺序(转自http://blog.csdn.net/mrzhoug/article/details/51581994)


    一、类加载做了哪些事?

      之前没有进行类加载
     1.类加载,同时初始化类中静态的属性(赋默认值)
     2.执行静态代码块
     3.分配内存空间,同时初始化非静态的属性(赋默认值)
     4.如果声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
     5.执行匿名代码块
     6.执行构造器  
     7.返回内存地址

    二、类加载的顺序

     1.static 变量
     2.static 代码块
     3.成员变量
     4.匿名块
     5.构造器
     ps:先加载父类,再加载子类;

    三、一个具体说明的例子

    其中D继承C,C继承B,E,F是单独的类,主方法在ExaminationDemo 中。

    public class ExaminationDemo {
    	public static void main(String[] args) {
    		System.out.println("1运行 ExaminationDemo 中的 main 函数, 创建 D 类实例");
    		new D();
    	}
    }
    
    class E {
    	// E的构造器
    	E() {
    		System.out.println("8执行 E 的构造函数");
    	}
    
    	// E的普通成员方法
    	public void funcOfE() {
    		System.out.println("12执行 E 的函数");
    	}
    }
    
    class F {
    	// F的构造器
    	F() {
    		System.out.println("2执行 F 的构造函数");
    	}
    
    	// F的普通成员方法
    	public void funcOfF() {
    		System.out.println("4执行 F 的函数");
    	}
    }
    
    class B {
    	//new了一个E对象
    	E e = new E();
    	//B的静态成员变量
    	static F f = new F();
    	//B的普通成员变量
    	public String sb = getSb();
    	//B的静态代码块
    	static {
    		System.out.println("3执行 B 类的 static 块(B 包含 E 类的成员 变量,包含静态 F 类成员变量)");
    		f.funcOfF();
    	}
    	//B的匿名代码块
    	{
    		System.out.println("10执行 B 实例的普通初始化块");
    	}
    	//B的构造器
    	B() {
    		System.out.println("11执行 B 类的构造函数(B 包含 E 类的成员变 量,包含静态 F 类成员变量)");
    	}
    	//B的普通成员方法
    	public String getSb() {
    		System.out.println("9初始化 B 的实例成员变量 sb");
    		return "sb";
    	}
    }
    
    class C extends B {
    	// C的静态代码块
    	static {
    		System.out.println("5执行 C 的 static 块(C 继承 B)");
    	}
    	// C的匿名代码块
    	{
    		System.out.println("13执行 C 的普通初始化块");
    	}
    
    	// C的构造器
    	C() {
    		System.out.println("14执行 C 的构造函数(C 继承 B)");
    	}
    }
    
    class D extends C {
    	// D的静态成员变量
    	public String sd1 = getSd1();
    	// D的普通成员变量
    	public static String sd = getSd();
    	// D的静态代码块
    	static {
    		System.out.println("7执行 D 的 static 块(D 继承 C)");
    	}
    	// D的匿名代码块
    	{
    		System.out.println("16执行 D 实例的普通初始化块");
    	}
    	// D的构造器
    	D() {
    		System.out.println("17执行 D 的构造函数(D 继承 C);父类 B 的实 例成员变量 sb 的值为:" + sb + ";本类 D 的 static 成员变量 sd 的值为:" + sd
    				+ "; 本类 D 的实例成员变量 sd1 的值是:" + sd1);
    	}
    	// D的静态成员方法(调用时才加载)
    	static public String getSd() {
    		System.out.println("6初始化 D 的 static 成员变量 sd");
    		return "sd";
    	}
    	// D的普通成员方法
    	public String getSd1() {
    		System.out.println("15初始化 D 的实例成员变量 sd1");
    		return "sd1";
    	}
    }

    从上面的代码我们可以看出,加载主方法时,先执行了输出语句,然后是new D(),由此进行加载顺序的分析。

    执行结果如下:

    /*
     * 1运行 ExaminationDemo 中的 main 函数, 创建 D 类实例 
     * 2执行 F 的构造函数 
     * 3执行 B 类的 static 块(B 包含 E类的成员 变量,包含静态 F 类成员变量) 
     * 4执行 F 的函数 
     * 5执行 C 的 static 块(C 继承 B) 
     * 6初始化 D 的 static 成员变量sd 
     * 7执行 D 的 static 块(D 继承 C) 
     * 8执行 E 的构造函数 
     * 9初始化 B 的实例成员变量 sb 
     * 10执行 B 实例的普通初始化块
     * 11执行 B 类的构造函数(B 包含 E 类的成员变 量,包含静态 F 类成员变量) 
     * 13执行 C 的普通初始化块 
     * 14执行 C 的构造函数(C 继承B) 
     * 15初始化 D 的实例成员变量 sd1 
     * 16执行 D 实例的普通初始化块 
     * 17执行 D 的构造函数(D 继承 C);父类 B 的实 例成员变量 sb的值为:sb;本类 D 的 static 成员变量 sd 的值为:sd; 本类 D 的实例成员变量 sd1 的值是:sd1
     */

    这个结果是怎么来的,看下图:

    总体上还是按照二的加载顺序来的,需要注意的是,当遇到子父类时,加载顺序是:

    父类的静态成员变量,静态代码块——>子类的静态成员变量,静态代码块——>父类的普通成员变量,匿名代码块,构造器

    ——>子类的普通成员变量,匿名代码块,构造器

    静态变量,和静态代码块的顺序先后,主要看谁先写,先写的先加载

    new对象和成员变量的顺序,谁写在前面就先加载谁(我理解的是两者都属于成员变量

    成员方法不调用就不加载

    而,当成员变量是方法时,就要调用该方法。

    无论是静态成员方法,还是普通成员方法,只有在调用时才被加载

    四、类加载前和加载后的执行顺序(转自http://blog.csdn.net/mrzhoug/article/details/51581994

    如果类还没有被加载: 
    1、先执行父类的静态代码块和静态变量初始化,并且静态代码块和静态变量的执行顺序只跟代码中出现的顺序有关。 
    2、执行子类的静态代码块和静态变量初始化。 
    3、执行父类的实例变量初始化 
    4、执行父类的构造函数 
    5、执行子类的实例变量初始化 
    6、执行子类的构造函数 

    如果类已经被加载: 
    则静态代码块和静态变量就不用重复执行,再创建类对象时,只执行与实例相关的变量初始化和构造方法。

    展开全文
  • java ClassLoader的学习  java是一门解释执行的语言,由开发人员编写好的java源文件先编译成字节码文件.class...  一个类如果要被JVM所调度执行,必须先把这个类加载到JVM内存里,java.lang下有个很重要的类ClassL
  • Java类加载顺序

    千次阅读 2020-04-28 15:34:17
    在日常工作中,比较少的机会会探究类加载机制,类加载顺序,但是这部分在代码优化,深入学习jvm有着极大的帮助,本文帮助不太了解类加载顺序的人从代码层级了解类加载顺序。 示例1-普通类 class Foo{ private int x...

    引言

    在日常工作中,比较少的机会会探究类加载机制,类加载顺序,但是这部分在代码优化,深入学习jvm有着极大的帮助,本文帮助不太了解类加载顺序的人从代码层级了解类加载顺序。

    示例1-普通类

    class Foo{
        private int x;
        {
            System.out.println("1");
        }
        static {
            System.out.println("2");
        }
    
        public Foo() {
            System.out.println("3");
            System.out.println("x=" + x + ",z=" + z);
        }
    
        public Foo(int x) {
            super();
            System.out.println("4");
            this.x = x;
        }
    
        static int z = 8;
    
        public void say() {
            System.out.println("5");
        }
    
        public static void say2() {
            {
                System.out.println("6");
            }
            System.out.println("7");
        }
    }
    public class App {
        public static void main(String[] args) {
           Foo.say2(); 
        }
    }
    

    很多人拿到这样的代码就会打颤,别着急,接着向下看,如果你耐心读完此文,相信再遇到此类的问题,乃至面试题都会如鱼得水,整理的头头是道!

    java类中包含的成员

    首先,我们了解一下java类中包含的成员有哪些

    成员解释
    成员变量类的直属变量
    方法类的直属方法
    局部变量方法体中定义的变量和方法中涉及的变量
    代码块方法体中定义的代码块
    构造代码块类的直属方法跨
    静态块类的直属方法块
    静态方法static修饰的方法
    静态成员变量static 修饰的类的直属变量
    成员内部类在一个类内部进行其他类结构的嵌套操作
    局部内部类定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内
    匿名内部类平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护,例如子线程的runnable对象
    静态内部类定义在了成员位置上,并且使用static来去修饰的类
    类实例化顺序

    了解了类的成员信息后,下面讲下类实例化顺序

    1. 执行静态块
    2. 执行构造代码块(构造代码块一定在构造器前执行)
    3. 执行构造器

    了解了这些之后,可能有些细心地童鞋会有疑问,还有静态成员变量呢?普通成员变量呢?他们什么时候执行?
    我们可以把静态成员变量看做静态块,普通成员变量看做构造代码块来处理,这样是不是简单很多。
    在这里插入图片描述

    类加载的顺序
    1. 调用构造器
    2. class.forname
    3. 调静态字段
    4. 调静态方法
    public class AppTest {
        public static void main(String[] args) {
            f1();
        }
    
        static AppTest t = new AppTest();
        static {
            System.out.println("1");
        }
        {
            System.out.println("2");
        }
    
        AppTest() {
            System.out.println("3");
            System.out.println("a=" + a + ",b=" + b);
        }
    
        public static void f1() {
            System.out.println("4");
        }
    
        int a = 110;
        static int b = 112;
    }
    
    

    类加载的时机

    类从被加载到虚拟机内存中开始,到卸载出内存为止,

    内部类

    (1)、内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 。
    (2)、内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的 。
    (3)、内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量 。

    子类C继承父类F

    当调用子类静态方法时,执行顺序为

    1. 按照继承顺序从顶级父类开始加载类
    2. 执行子类C中的静态块
    3. 执行子类静态块
    4. 执行调用方法

    当调用子类非静态方法

    1. 初始化(加载)所有超类
    2. 加载自身静态块
    3. 再次初始化所有超类及自身
    4. 执行调用方法
    解释
    1. 按照继承顺序从顶级父类开始所加载类(按照顶级类到子类开始把相关类加载进内存中)
    2. 因为使用了new,所以会创建一个全新的子类对象,所以之前的超类由于连带关系均需要为了创建新的子类对象而再次执行一遍类加载,但是静态块已经加载完毕,无需再次加载,所以此时仅加载构造代码块和构造器
    3. 得到引用对象,执行代码

    接口

    1. 在有继承关系的类中引用多个不同接口时,需要注意接口内定义的常量名称不可重复,如果重复在调用常量时,编译器会提示The field Father.v is ambiguous(所使用的字段不明确)
    2. 接口内的常量初始化是在被调用时初始化,当且仅当仅调用接口中的某常量时,运行且不会影响实现接口的类,例如类Foo实现了接口Bar,Bar中有常量S,使用Foo.S仅会初始化S,不会调用Foo内部任何成员。
    展开全文
  • Tomcat的类加载顺序的实现

    千次阅读 2019-09-15 11:38:56
    Tomcat的类加载顺序的实现   其实之所以为写这篇文章的原因,主要是前段时间,因为使用第三方的一些库,需要将这些库放到JDK的ext目录,由Java的ExtClassLoader进行加载,第三方库也是用了日志框架,而且使用的是...

    Tomcat的类加载顺序的实现

      其实之所以为写这篇文章的原因,主要是前段时间,因为使用第三方的一些库,需要将这些库放到JDK的ext目录,由Java的ExtClassLoader进行加载,第三方库也是用了日志框架,而且使用的是slf4j,所以要使用这个第三方库,必须将slf4j-api.jar这个slf4j的接口包放到ext目录下。将这个日志放到slf4j-api.jar放到ext目录下,接着应用层的日志打印不出来,应用层的日志包也是使用slf4j,应用是在Tomcat中跑的。

    为什么打印不了日志

      上面的问题,在Debug日志包和查找类加载相关的资料,最终发现与类加载相关;实际上一直使用的slf4j-api.jar是ext目录下,而它的实现包在应用层,即项目中引入的,ExtClassLoader加载的slf4j-api.jar日志包,根据类加载的特性不允许向下查找到它的实现包slf4j-simple.jar,所以实际上就会找不到实现,导致日志打印不出来问题。如下示意图,这是类加载的特性和顺序决定的,具体可以参考另外一篇关于Java的类加载器的文章:https://blog.csdn.net/strive_or_die/article/details/98664519。
    在这里插入图片描述

    Tomcat的类加载机制

    应用服务器一般都会自行创建类加载器来实现更加灵活的控制,Tomcat服务器也不例外。

    • 隔离性:Web应用类库互相隔离,避免依赖库或者应用包相互影响。因为一个Tomcat服务器可以跑多个应用,不同的应用对于同一个包所依赖的版本可能不一样,例如有的slf4j-api包的版本是1.6.x,而有的应用是1.8.x,那么这样必然会导致部分应用的日志无法打印。
    • 灵活性:Web应用之间的类加载器互相独立,那么就可以针对一个Web应用进行重新部署,此时Web应用的类加载器会重新创建并且不会影响其他Web应用。
    • 性能:由于每个Web应用都有一个类加载器,因此Web应用在加载类时,不会搜索其他Web应用包含的Jar包,这样性能是比只有一个累加器的情况要好的。

    先看一下Tomcat的类加载器的图示:
    在这里插入图片描述
      从上图中可以看到,除了每个Web应用加载器外,Tomcat也提供了3个基础的类加载器,这3个类加载器指向的路径和包列表都可以再catalina.properties中配置。

    • Common Class Loader:以System类加载器为父类加载器,位于Tomcat应用夫妻顶层的公用类加载器。它的路径为commmon.loader,默认指向的是$CATALINA_HOME/lib下的包。
    • Catalina Class Loader:以Common类加载器为父类加载器,用于加载Tomcat应用服务器的累加器,其路径为server.loader,默认为空。此时Tomcat使用Common类加载器加载器应用服务器。
    • Shared Class Loader:以Common类加载器为父类加载器,是所有父类应用加载器的父类加载器,其路径为为shared.loader,默认为空,此时此时Tomcat使用Common类加载器作为Web应用的父类加载器。
    • Web Application Class Loader:以Shared为父类加载器,加载/WEB-INF/classes目录下的未压缩的Class和资源文件以及/WEB-INF/lib目录下的Jar包。特别注意:该类加载器只对当前Web应用可见,对其他Web应用都不可见。

      首先,Common类加载器负责加载Tomcat的应用服务器了内部和Web应用均可见得类,例如Servlet相关包和一些通用的工具包。

      其次,Catalina类加载器负责加载只有Tomcat应用服务器内部可见的类,这些类对Web应用不可见。如Tomcat的具体实现类,因为我们的Web应用最好和服务器松耦合。

      再次,Shared累加器负责加载Web应用共享的类,这些类Tomcat服务器不会依赖。

      最后,Tomcat服务器$CATALINA_HOME/bin目录下的包作为启动入口由System类加载器加载。通过将者几个启动包剥离,Tomcat简化了应用服务器的启动,同时增加了灵活性。

    Web应用类加载器顺序(关键)

    Tomcat的默认加载机制是委派模式,委派过程如下。

    • 1.从缓存中加载
    • 2.如果缓存中没有,则从父类加载器中加载。
    • 3.如果父类加载器没有,则从当前类加载器加载。
    • 4.如果没有,则抛出异常。

      Tomcat提供的Eeb应用类加载器和默认的委派模型有些地方是不同的。当进行类加载是,除了JVM基础类库外,他首先会尝试通过当前累加器加载,然后才进行委派。另外一点需要注意的是:Servlet规范相关API禁止通过Web应用类加载器加载,因此,不要在Web应用中包含这些包,例如javax.servlet-api.jar

    Web应用加载器默认加载顺序如下:

    • 1.从缓存中加载
    • 2.如果没有,则从扩展类加载器和Bootstrap类加载器中加载。
    • 3.如果没有,则从当前类加载器加载(按照WEB-INF/classes、WEB-INF/lib的顺序)。
    • 4.如果没有,则从父类加载器加载,由于父类加载器采用默认的委派模型,所以加载顺序为System、Common、Shared。

      Tocmat提供了delegate属性用于控制是否启用Java委派模型,默认为false(不启用)。当配置为true时,Tomcat将使用默认委派模型,即按如下顺序加载。

    • 1.从缓存中加载
    • 2.如果没有,则从扩展类加载器和Bootstrap类加载器中加载。
    • 3.如果没有,则从父类加载器加载,加载顺序为System、Common、Shared。
    • 4.如果没有,则从当前类加载器加载(按照WEB-INF/classes、WEB-INF/lib的顺序)。

    如何解决日志包加载问题

      通过前面的Tomcat的类加载器知识,我们可以知道为啥那个在ext目录下的slf4j-api.jar会使得应用的jar打印不出来,因为即使Tomcat实现了自己的类加载器,但是对于Web应用类加载器来说,扩展类加载器和Bootstrap类加载器是优先于它进行加载的,所以应用中的slf4j-api.jar实际并没有被加载,ext目录下的被加载了,但是正常的类加载是没法实现父类加载器向子类加载器方向去寻找实现的,所以slf4j-api.jar找不到实现了,打印不出日志。

      那该怎么解决呢,有了上面的知识,我们可以知道如果Tomcat的delegate属性为false时,这也是默认值来的,Web应用加载器优先于System、Common、Shared类加载器,那么我们可以将第三方库和它所以来的slf4j-api.jar包移动到Common类加载器加载的目录$CATALINA_HOME/lib下,那么就可以保证使用的slf4j-api.jar是应用中引入的了,这样我们的应用就可以打印出日志了。特别注意:这时候的第三方库的日志是打印不出来的,因为它依旧没法找到它的日志实现包,除非将它的实现包也放到$CATALINA/lib下。

    参考

    Tomcat 架构解析 刘光瑞 著.
    https://tomcat.apache.org/tomcat-9.0-doc/class-loader-howto.html

    展开全文
  • 目录 为什么是null 回顾类加载 原因 问题重现 总结 类加载顺序 子类重写被父类构造函数调用的函数注意 不能放过不起眼的日志 一条日志引发的案子 [11:12:58.505][D][Gen][RTLive][getIns ins = 4414717] [11:12:58....
  • SpringBoot控制配置类加载顺序

    千次阅读 2020-06-12 10:33:18
    1、@Configuration配置被加载进容器的方式大体上可分为3种 ...2.1、传统的 Spring Framework 控制配置类加载顺序 在传统的 Spring Framework 里,一个 @Configuration 注解标注的类就代表一个配置类,当存在多
  • 详细说明了tomcat启动过程中 加载资源的顺序
  • 主要介绍了如何正确控制springboot中bean的加载顺序总结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 有时候需要在普通获取到Bean,但是new出来的对象不会被注入,这时就需要通过SpringBoot上下文去获取Bean,思路很简单,实现ApplicationContextAware后在上加个@Componse注解就可以很容易的实现加载上下文,例如 ...
  • 查看类加载路径:开启verbose class loading Servers > Server Types(WebSphere application servers) > server1 > Configuration > Server Infrastructure > Java and Process Management(Process ...
  • 一、单独类加载顺序 静态代码块最先加载,加载顺序和代码出现顺序有关,从上到下依次执行 非静态代码块加载,加载顺序和代码出现顺序有关,从上到下依次执行 构造方法 一般方法,加载顺序跟调用顺序有关 父类静态...
  • 1.SpringBoot的类加载顺序怎么能知道? 在Bean的默认构造函数里面使用System.out.println就能知道创建顺序。 2.一个问题:SpringBoot的main函数中能够使用IOC容器中的Bean吗? 可以,但是是在run程序之后,通过...
  • 类加载顺序问题导致@Autowired无法自动注入 今天在学习的过程中遇到了@Autowired无法自动注入的问题,追寻原因是类加载顺序导致,下面将详细探究这个问题。 首先来看出现问题的代码,一个普通类,一个测试类。 @...
  • java静态内部类加载顺序

    千次阅读 2018-12-13 21:43:01
    静态内部类加载顺序 我们先来区分一下两个概念:类加载、加载。 类加载的过程包括加载,初始化,验证,解析,准备,初始化等五个过程。加载是类加载的一部分。 区分完这两个概念之后我们再来看下面的问题。 我们...
  • JAVA CLASS PATH 的同名类加载顺序

    千次阅读 2019-08-05 18:41:45
    java的classpath中如果出现了同名,同名的的代码不同,那只用的到底是哪一个呢? java -cp ./;./classes;./xptest.jar com.ssc.xp.ws.TestClassLoaderA Java代码 packagecom.ssc.xp.ws; ...
  • websphere服务器类加载顺序

    千次阅读 2018-06-26 08:44:45
    当时项目组不知道是什么原因造成的,重启之后就会报这个错误。后来查了好多相关资料,发现是服务器配置问题导致违反装入约束。解决办法:通过先加载子类再加载父类的配置之后,就把这个问题解决啦!...
  • 主要给大家介绍了关于tomcat8改了jar加载顺序的踩坑记录,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • 依赖冲突: 当dependency的groupId和artifactId一样但version不同时...重复类的类加载: 当前POM的依赖,顺序按照pom.xml内使用的顺序(由上至下,靠上优先加载) 父POM使用的依赖会被最后加载 参考博客
  • spring boot类加载顺序问题调试

    千次阅读 2018-12-20 19:56:49
    然后启动后,可以打印出详细的类加载项目,就可以查找处理到底是哪个依赖没有成功导致项目启动失败的 [Loaded org.springframework.web.servlet.mvc.condition.ProducesRequestCondition from file:/D:/tools/...
  • 使用@Order注解调整配置类加载顺序

    千次阅读 2019-08-20 15:36:29
    1、Spring 4.2 利用@Order控制配置加载顺序, 2、Spring在加载Bean的时候,有用到order注解。 3、通过@Order指定执行顺序,值越小,越先执行 4、@Order注解常用于定义的AOP先于事物执行 2 、新建...
  • 主要介绍了详解Spring Boot 配置加载顺序及属性加载顺序,本章内容基于 Spring Boot 2.0 进行详解,感兴趣的朋友跟随脚本之家小编一起看看吧
  • 加载一个时,其内部是否同时被加载?下面我们做一个实验来看一下。  Java代码  public class Outer {   static {   System.out.println("load outer class...");   }     /...
  • 加载顺序

    千次阅读 2021-08-14 20:08:25
    1 类加载从上往下执行,依次执行静态的初始化语句和初始化块,而且类加载优先于对象创建。(静态初始化语句和初始化块只加载一次) 2创建本类的对象时:从上往下执行一次非静态的初始化语向和初始化块,然后执行构造...
  • jvm类加载顺序

    千次阅读 2017-11-29 16:52:59
    JVM类加载顺序 今天,梳理一下类加载顺序,从测试结果来推测类加载顺序。 第一步:基础准备 父类: public class Parent { public String parentProperty="ParentPropertyValue"; public static ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 457,979
精华内容 183,191
关键字:

类的加载顺序

友情链接: Winform.zip