Pandora Bootstrap源码分析
程序员文章站
2023-12-30 18:33:52
...
@SpringBootApplication
public class HSFProviderApplication {
public static void main(String[] args) {
// 启动 Pandora Boot 用于加载 Pandora 容器
PandoraBootstrap.run(args);
SpringApplication.run(HSFProviderApplication.class, args);
// 标记服务启动完成,并设置线程 wait。防止用户业务代码运行完毕退出后,导致容器退出。
PandoraBootstrap.markStartupAndWait();
}
}
就这么一个简单的代码,都可以实现classloder的隔离,为什么这么神奇呢?
在我的认知里,是没有办法改变当前的classloder的,当前的 SpringApplication.run的时候,肯定是系统的classloder啊,就让我们来揭开迷雾吧。
PandoraBootstrap.run的时候,调用的核心方法:
参数mainClass就是HSFProviderApplication这个有main方法的入口类
参数args就是main方法的参数
参数的classLoader是我们自己创建的classloader
public static void reLaunch(String[] args, String mainClass, ClassLoader classLoader) {
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(mainClass);
Thread launchThread = new Thread(threadGroup, new LaunchRunner(mainClass, args), "main");
launchThread.setContextClassLoader(classLoader);
launchThread.start();
LaunchRunner.join(threadGroup); //main方法执行的线程会一直阻塞在这里
threadGroup.rethrowUncaughtException();
}
可以看到是创建了一个新的线程,这个线程做的事情很简单,就是用传入的classloader,执行我们的main方法
public void run() {
...
Class<?> startClass = classLoader.loadClass(this.startClassName);
Method mainMethod = startClass.getMethod("main", String[].class);
if (!mainMethod.isAccessible()) {
mainMethod.setAccessible(true);
}
mainMethod.invoke((Object)null, this.args)
这样就做到了偷天换日,最开始的main方法的线程是阻塞在那里的,并没有继续往下走,然后Pandora Bootstrap启动了一个新的线程,用新创建的classloder来执行main方法。
由于我们创建的classloder是系统classloder的子类,我们就可以做文章了,中间件的类用新创建的classloder来加载,业务的类用系统的classloder来加载。是不是非常巧妙啊。
也就是说,第一行代码PandoraBootstrap.run(args)是执行了两遍.
如何保证不会执行多次加载逻辑,甚至死循环的呢?第一遍是系统的classloder,第二遍虽然看上去是我们自己创建的classloder,但我们我们创建的classloder是委托给系统的classloder的,所以其实还是相同的classloder。这就很简单了,PandoraBootstrap执行第一遍之后就改一个bool变量,第二遍读到这个变量改了就直接跳过了。
if (SarLoaderUtils.unneedLoadSar()) {
LogConfigUtil.initLoggingSystem();
} else {
URL[] urls = ClassLoaderUtils.getUrls(PandoraBootstrap.class.getClassLoader());
if (urls == null) {
throw new IllegalStateException("Can not find urls from the ClassLoader of PandoraBootstrap. ClassLoader: " + PandoraBootstrap.class.getClassLoader());
} else {
urls = AutoConfigWrapper.autoConfig(urls);
ReLaunchMainLauncher.launch(args, deduceMainApplicationClass().getName(), urls);
}
}