@SpringBootApplication
public class HSFProviderApplication {
public static void main(String[] args) {
// 啟動(dòng) Pandora Boot 用于加載 Pandora 容器
PandoraBootstrap.run(args);
SpringApplication.run(HSFProviderApplication.class, args);
// 標(biāo)記服務(wù)啟動(dòng)完成,并設(shè)置線程 wait值桩。防止用戶業(yè)務(wù)代碼運(yùn)行完畢退出后丽旅,導(dǎo)致容器退出。
PandoraBootstrap.markStartupAndWait();
}
}
就這么一個(gè)簡單的代碼懒浮,都可以實(shí)現(xiàn)classloder的隔離飘弧,為什么這么神奇呢?
在我的認(rèn)知里嵌溢,是沒有辦法改變當(dāng)前的classloder的眯牧,當(dāng)前的 SpringApplication.run的時(shí)候,肯定是系統(tǒng)的classloder啊赖草,就讓我們來揭開迷霧吧学少。
PandoraBootstrap.run的時(shí)候,調(diào)用的核心方法:
參數(shù)mainClass就是HSFProviderApplication這個(gè)有main方法的入口類
參數(shù)args就是main方法的參數(shù)
參數(shù)的classLoader是我們自己創(chuàng)建的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方法執(zhí)行的線程會(huì)一直阻塞在這里
threadGroup.rethrowUncaughtException();
}
可以看到是創(chuàng)建了一個(gè)新的線程秧骑,這個(gè)線程做的事情很簡單版确,就是用傳入的classloader,執(zhí)行我們的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方法的線程是阻塞在那里的绒疗,并沒有繼續(xù)往下走,然后Pandora Bootstrap啟動(dòng)了一個(gè)新的線程骂澄,用新創(chuàng)建的classloder來執(zhí)行main方法吓蘑。
由于我們創(chuàng)建的classloder是系統(tǒng)classloder的子類,我們就可以做文章了,中間件的類用新創(chuàng)建的classloder來加載磨镶,業(yè)務(wù)的類用系統(tǒng)的classloder來加載溃蔫。是不是非常巧妙啊。
也就是說琳猫,第一行代碼PandoraBootstrap.run(args)是執(zhí)行了兩遍.
如何保證不會(huì)執(zhí)行多次加載邏輯伟叛,甚至死循環(huán)的呢?第一遍是系統(tǒng)的classloder脐嫂,第二遍雖然看上去是我們自己創(chuàng)建的classloder统刮,但我們我們創(chuàng)建的classloder是委托給系統(tǒng)的classloder的,所以其實(shí)還是相同的classloder账千。這就很簡單了侥蒙,PandoraBootstrap執(zhí)行第一遍之后就改一個(gè)bool變量,第二遍讀到這個(gè)變量改了就直接跳過了蕊爵。
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);
}
}