场景:
项目采用springboot+devtools热加载开发,在项目的某个角落有一个工具类,采用静态内部类做InstanceHolder,如下:
public final class ConnectionTool {
private ConnectionTool() {}
public static final ConnectionTool getInstance() {
return MyHolder.getInstance();
}
public final class MyHolder {
private MyHolder() {}
private static final Object SYNC_OBJ = new Object();
private static ConnectionTool instance;
public static ConnectionTool getInstance() {
final ConnectionTool pool;
synchronized(SYNC_OBJ) {
if(instance == null) {
instance = new ConnectionTool();
}
pool = instance;
}
return pool;
}
}
}
在实际调用的时候,有两个地方,一个是controller中获取instance,另一个是schedule方法中获取instance。但是两个地方获取到的instance竟然不是同一个,换言之,本以为万无一失的单例终极之道竟然失效。。。忧伤
经过重重筛查,最终发现,两个地方的类的ClassLoader竟然不是同一个,controller中的classCloader是sun.misc.Launcher$AppClassLoader@18b4aac2,而线程中的是org.springframework.boot.devtools.restart.classloader.RestartClassLoader@68801664,换言之,这个类被加载了两次,so。
解决方案:
1. 去掉devtools,问题不攻自破
2. 用系统类加载器+反射来获取instance,如下:
try {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<?> aClass = systemClassLoader.loadClass("com.a.b.c.ConnectionPool");
Method getInstance = MethodUtils.getMatchingMethod(aClass, "getInstance");
ConnectionPool invoke = (ConnectionPool)getInstance.invoke(null);
System.out.println(aClass);
System.out.println(aClass.getClassLoader());
}catch (Exception e) {
e.printStackTrace();
}
from now on, everything will be ok, good lucks!
版权声明:本文为tongyimplin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。