emmmm标题有点今日某条的,其实我想表达的意思是Spring Boot项目打成的jar包,程序的真正入口main方法并不是我们写的ApplicationRun启动类里的。当然,这看怎么理解了,如果非要说这是我自己写的main方法它就是入口这也没错,只不过你这main方法前面人家jar包还有一个自己的入口呢。
先说我是怎么发现这个问题的吧,最近写了一个小应用,当然用Spring Boot开发的,打成jar包后在本地执行没啥问题。然后我想把它发给别人,即使人家电脑上没有jre环境也能执行这个jar包,这就需要连同jar包带着jre一起打包成exe程序了,然后就下载了exe4j一顿操作,具体操作步骤感兴趣的可以去搜一下,然后重点来了。
执行到这一步的时候
这一步是刚刚选好了jar包,准备配置入口mainClass,共有四个选项,本着其他三个没见过Application自己写的比较亲的理由,选了Application,然后又是一顿操作exe文件句生成了,兴奋的运行了一下,哦吼...
咋回事,这不明摆着的还没找到?然后就懵了一会,一度想明天再看。最后把其他三个一个一个试了一下(其实看名字第一个就猜对了,试其它两个为了确认一下),发现下面这个mainClass才是正确的入口选项
找到了正确的入口方法,小应用也跑了起来,但是这个问题还在,于是就去了解了一下为什么是这个JarLauncher。
为什么是JarLauncher这个问题可以有两个回答:
1.简单粗暴直接反编译spring boot的可执行jar,看MANIFEST.MF文件。该文件记录了jar包的一些配置信息,其中就有mainClass,如下图:
文件最后一行白纸黑字写了mainClass是JarLauncher。
2.都知道spring boot是有自己的打包插件spring-boot-maven-plugin的,
这是一个boot自定义的maven插件,其打成的jar又称fat jar,和maven自带的maven-jar-plugin有区别。要想知道为什么入口是JarLauncher那就从源头开始,看看spring boot是如何打包的。于是就反编译了这个自定义打包插件。结构如图:
文件还是比较少的,之前有了解到spring boot的打包方式其实就是一个repackage重新打包的过程,所以这次源码撸的还算顺利,直接找到了RepackageMojo这个类文件。里面第一个方法就是excute()方法,看来是入口了。
可以看出前两个if检查后核心在repackage()方法,这个方法也是紧跟其后。
这里面东西比较多,Artifact source拿到的是项目唯一标识信息,这个在我们建maven项目的时候有遇到过。File target拿到的是项目源文件,打包嘛,没东西怎么打。需要重点关注的是这个Repackager repackager = getRepackager(source.getFile()),看样子这个repackager应该是个正儿八经的打包工具,东西都拿到后try语句块中调用repackager.repackage()开始打包也能看出来。那就看一下这个getRepackager()方法,是怎么拿到这个打包工具的。
这里面就是根据项目唯一标识source和layoutFactory样式new了一个Repackage对象。然后有一个setMainClass()。嗯哼这不就是我想找的嘛?兴奋的点了一下这个变量看是怎么来的,结果发现:
打了@Parameter注解 ,看来是从配置文件里来的,忽然想到之前用maven自己的打包插件打包好像确实要指定mainClass,这就很失望了,配置文件里指定的不就是自己写的启动类嘛。源码一顿撸到最后把自己结论推翻了?没有没有还没完,接着又看了看这个layoutFactory,这个layout可以看下有哪几种:
意思就是打包成什么嘛,我们这里肯定是JAR。 然后就想看看这个JAR类是什么,看引用这个类在另一个jar包里。
接着反编译了这个spring-boot-loader-tools,一眼就找到了这个JAR,还找到了刚刚看到的打包工具类Repackager:
不过我已经对这个打包工具怎么打包已经不感兴趣了,看一下这个JAR:
还是很清晰的,到这里也找到答案了,getLauncherClassName()方法顾名思义返回的是入口类文件路径,返回的就是这个JarLauncher,意思就是jar包的入口就是JarLauncher。同时还在想困惑之前的那个setMainClass(),就想文章开头提到的:
哈哈截了张本文的图,大不了就理解成mainClass(自己写的入口类)和launcherClass(JarLauncher)吧, launcherClass在更外面,这样就清晰多了。
我们的fat jar反编译后可以找到这个JarLauncher:
感兴趣的可以去深究一下。