buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

springboot有两个主启动类时,maven打包(可执行包)会报错,需指定启动主类

 

我本地写了一个rabbitmq fanout模式的demo。consumer启动类和producer启动类都放到了一个springboot程序里。本地调试通过。

 

 

突然有个疑问,springboot项目是怎么来发现主启动类的呢?

 

 

我们知道,默认使用maven打包时,是个普通的可供依赖的jar包,仅包含来自项目源的资源和已编译的Java类,并不能单独运行。

鉴于上面的疑问,我在pom里加上spring-boot-maven-plugins,让这个项目作为一个springboot程序。

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

这时,执行mvn package或mvn install命令时,发现报错--无法从两个候选的main程序类来选定一个作为主启动类。具体错误信息如下:
Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.2.0.RELEASE:repackage (repackage) on project fanoutx: Execution repackage of goal org.springframework.boot:spring-boot-maven-plugin:2.2.0.RELEASE:repackage failed: Unable to find a single main class from the following candidates [com.fanoutconsumer.FanoutConsumerApplication, com.fanoutproducer.FanoutProducerApplication]

这才知道,在这种情况下,需要通过maven-plugin的configuration/mainClass节点来指定主启动类。 

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>com.fanoutconsumer.FanoutConsumerApplication</mainClass>
            </configuration>
        </plugin>
    </plugins>
</build>

 

当然,话说回来,在企业应用中,我们通常也不会在一个springboot程序里写两个或多个启动类(只会写一个),所以也就不用显式指定启动类了。

 

 

springboot可执行程序包默认也是.jar。那么,对于同一个springboot项目,打出来的普通可依赖jar与springboot可执行jar有什么区别呢?

我们看两种情况下的jar包里的文件结构及文件目录

1/2)作为普通可依赖jar包的 fanoutx-1.0-SNAPSHOT.jar

fanoutx-1.0-SNAPSHOT.jar
	application.yml
	+com(项目已编译类,.class文件)
	+META-INF
		MANIFEST.MF
		+maven
			+org.example
				+fanoutx
					pom.properties
					pom.xml

其中,META-INF/MANIFEST.MF内容为:

Manifest-Version: 1.0
Implementation-Title: fanoutx
Implementation-Version: 1.0-SNAPSHOT
Build-Jdk-Spec: 1.8
Created-By: Maven Archiver 3.4.0

 

2/2)作为springboot可执行jar包 的 fanoutx-1.0-SNAPSHOT.jar 是一个“胖jar”(有时称为“fat jars”)

fanoutx-1.0-SNAPSHOT.jar
    +BOOT-INF
        +classes
            application.yml
            +com(项目已编译类,.class文件)
        +lib (项目依赖的lib包)
    +META-INF
        MANIFEST.MF
        +maven
            +org.example
                +fanoutx
                    pom.properties
                    pom.xml
    +org
        +springframework
            +boot
                +loader
                    +archive
                    +data
                    ExecutableArchiveLauncher.class
                    +jar
                    JarLauncher.class
                    LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class
                    LaunchedURLClassLoader.class
                    Launcher.class
                    MainMethodRunner.class
                    PropertiesLauncher$1.class
                    PropertiesLauncher$ArchiveEntryFilter.class
                    PropertiesLauncher$PrefixMatchingArchiveFilter.class
                    PropertiesLauncher.class
                    +util
                    WarLauncher.class

其中,META-INF/MANIFEST.MF内容为:

Manifest-Version: 1.0
Implementation-Title: fanoutx    
Implementation-Version: 1.0-SNAPSHOT
Start-Class: com.fanoutconsumer.FanoutConsumerApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.2.0.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher

 

可见,主要是后者有个 BOOT-INF ,还有 org/springframework/boot/loader/ 。---------->不仅具有来自项目的已编译Java类,还包括代码需要运行的所有 jar 依赖项,而且还具有启动Spring Boot应用程序所需的所有运行时库。
另外还可以看到,两者的 META-INF/MANIFEST.MF 文件里的内容不同,可执行jar里定义了Jarlauncher 及 Start-Class。

 

BTW,两者里,pom.xml内容与原始的pom.xml相同。pom.properties定义了artifact

version=1.0-SNAPSHOT
groupId=org.example
artifactId=fanoutx

 

 

【the end】

ref1:Maven-build之spring-boot-maven-plugin

ref2:baidu:Jarlauncher的实现原理

ref3:baidu:Springboot 创建可执行 Jar

posted on 2022-06-14 10:32  buguge  阅读(3307)  评论(0编辑  收藏  举报