这是我的Springboot启动的的图
如图所示:
com.essence.framework.support.jdbc.JdbcUtil这个类之前被初始化过一次,里面的静态属性已经被赋值了,但是后来调用的时候发现,静态属性值莫名变成null了。
于是我把调用堆栈信息打印了出来。
发现该类被clinit了两次!第一次是我人为控制初始化的,自然属性值什么的都赋值了。
但是为什么第二次使用的时候,又被clinit了呢?
关于clinit是这么解释的:
--引文--
虚拟机会保证一个类的clinit()方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的clinit()方法,其他线程都需要阻塞等待,直到活动线程执行clinit()方法完毕。需要注意的是,其他线程虽然会被阻塞,但如果执行clinit()方法的那条线程退出clinit()方法后,其他线程唤醒后不会再次进入clinit()方法。同一个类加载器下,一个类型只会初始化一次。
为什么我的JdbcUtil被clinit了两次。
----已解决,完结撒花---
错误原因,我引入了devtools依赖。
错误分析:我在jdbcUtil中添加static代码块,在其中输出了classloader。发现:第一次加载时候的classloader为:org.springframework.boot.devtools.restart.classloader.RestartClassLoader,第二次加载的时候的classloader为:sun.misc.Launcher$AppClassLoader。我的JdbcUtil第一次初始化静态全局变量是在ServletContextListener监听中,该监听中的所有类都是由devtools的RestartClassLoader加载的。后面使用jdbcUtil的时候,却是用AppClassLoader加载的,导致JdbcUtil被加载了两次,丢失了第一次加载时初始化的值。
结论: 删掉devtools依赖搞定,如果需要在启动监听中初始化值,就最好不要引用devtools,它自己实现了一个classloader,会导致你初始化的对象/值,因为后来类被AppClassLoader重新加载而丢失
因为你不是单例模式,或者说不是标准的单例模式,你的单例模式被其他类可以反射创建,或者是连个双重校验锁都没有,所以,当你的jdbcUtil在多个类里面使用的时候,就会被创建很多次,建议使用枚举来实现单例,至少保证双重校验锁,其实使用静态内部类单例也可以,双重校验锁是最基本的,但是已经out了,现在流行只有2中,一种是利用枚举实现单例,一种是用静态内部类,实现单例,具体实现方式,百度就知道,
在一个虚拟机环境下会保证一个类的clinit()方法执行一次,,,, 要保证你的执行是在同一个虚拟机下。。