欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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);
            }
        }

上一篇:

下一篇: