springboot装配devtools后,类被加载两次的问题

  • Post author:
  • Post category:其他


场景:

项目采用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 版权协议,转载请附上原文出处链接和本声明。