当前位置: 首页>编程语言>正文

为什么导入了SpringBootMaven插件打包结果不是SpringBoot的大Jar包,导致Jar包无法正常启动

最近遇到一个问题,自己直接通过Maven去建立一个SpringBoot项目,最终打包的结果却并不是SpringBoot的打包结果,导致Jar包不能正常通过java -jar启动。

项目依赖很简单,如下面的所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wanna</groupId>
    <artifactId>maven-test-2</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.15</version>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.5.15</version>
        </dependency>
    </dependencies>
</project>

只是简单导入了一个SpringBoot的WebStarter和一个SpringBoot的Maven编译器插件。

自己捣鼓了几天都没有结果,在网上无意间发现了一篇文章说,将Maven插件的配置改为如下的方式,就可以。

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.15</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

实验了一下,最终发现确实可以,能正常打包,也能正常运行Jar包。

可是为什么呢?为什么加上这样的配置之后就行。我之前的SpringBoot项目都是,直接导入插件就行。

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.15</version>
            </plugin>

刚刚我们的这个repackage是Maven插件的一个Mojo(RepackageMojo),于是我们需要弄清楚为什么这个Mojo没生效。

于是开始去Debug MAVEN源码,找到MAVEN当中构建Mojo的地方,代码位置如下。

org.apache.maven.lifecycle.internal.DefaultLifecycleMappingDelegate#calculateLifecycleMappings

    public Map<String, List<MojoExecution>> calculateLifecycleMappings( MavenSession session, MavenProject project,
                                                                        Lifecycle lifecycle, String lifecyclePhase )
        throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
        MojoNotFoundException, InvalidPluginDescriptorException
    {
        /*
         * Initialize mapping from lifecycle phase to bound mojos. The key set of this map denotes the phases the caller
         * is interested in, i.e. all phases up to and including the specified phase.
         */

        Map<String, Map<Integer, List<MojoExecution>>> mappings =
            new LinkedHashMap<>();

        for ( String phase : lifecycle.getPhases() )
        {
            Map<Integer, List<MojoExecution>> phaseBindings = new TreeMap<>();

            mappings.put( phase, phaseBindings );

            if ( phase.equals( lifecyclePhase ) )
            {
                break;
            }
        }

        /*
         * Grab plugin executions that are bound to the selected lifecycle phases from project. The effective model of
         * the project already contains the plugin executions induced by the project's packaging type. Remember, all
         * phases of interest and only those are in the lifecycle mapping, if a phase has no value in the map, we are
         * not interested in any of the executions bound to it.
         */

        for ( Plugin plugin : project.getBuild().getPlugins() )
        {
            for ( PluginExecution execution : plugin.getExecutions() )
            {
                // if the phase is specified then I don't have to go fetch the plugin yet and pull it down
                // to examine the phase it is associated to.
                if ( execution.getPhase() != null )
                {
                    Map<Integer, List<MojoExecution>> phaseBindings = mappings.get( execution.getPhase() );
                    if ( phaseBindings != null )
                    {
                        for ( String goal : execution.getGoals() )
                        {
                            MojoExecution mojoExecution = new MojoExecution( plugin, goal, execution.getId() );
                            mojoExecution.setLifecyclePhase( execution.getPhase() );
                            addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() );
                        }
                    }
                }
                // if not then i need to grab the mojo descriptor and look at the phase that is specified
                else
                {
                    for ( String goal : execution.getGoals() )
                    {
                        MojoDescriptor mojoDescriptor =
                            pluginManager.getMojoDescriptor( plugin, goal, project.getRemotePluginRepositories(),
                                                             session.getRepositorySession() );

                        Map<Integer, List<MojoExecution>> phaseBindings = mappings.get( mojoDescriptor.getPhase() );
                        if ( phaseBindings != null )
                        {
                            MojoExecution mojoExecution = new MojoExecution( mojoDescriptor, execution.getId() );
                            mojoExecution.setLifecyclePhase( mojoDescriptor.getPhase() );
                            addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() );
                        }
                    }
                }
            }
        }

        Map<String, List<MojoExecution>> lifecycleMappings = new LinkedHashMap<>();

        for ( Map.Entry<String, Map<Integer, List<MojoExecution>>> entry : mappings.entrySet() )
        {
            List<MojoExecution> mojoExecutions = new ArrayList<>();

            for ( List<MojoExecution> executions : entry.getValue().values() )
            {
                mojoExecutions.addAll( executions );
            }

            lifecycleMappings.put( entry.getKey(), mojoExecutions );
        }

        return lifecycleMappings;

    }

这里的一个MojoExecution对象,就是一个我们配置的Mojo,如果我们在plugin当中添加了如下这样的配置,那么这里就能构建出来repackage这样的一个MojoExecution,如果我们没有添加,则会缺少了这样的MojoExecution。

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.15</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

这里,我们开始怀疑,是在哪里悄悄的配置了repackage这个Mojo。

我们找到了SpringBootMavenPlugin的官方文档:

https://docs.spring.io/spring-boot/docs/3.2.2/maven-plugin/reference/htmlsingle/

在第三章"Using the Plugin"当中,有下面这样的介绍。

为什么导入了SpringBootMaven插件打包结果不是SpringBoot的大Jar包,导致Jar包无法正常启动,第1张

其中一条告诉了我们,我们如果继承了"spring-boot-starter-parent"的话,将会生成一个repackage goal的Execution(并且指定了executionId为repackage)。

我们找到了spring-boot-starter-parent这个POM,发现其中定义的spring-boot-maven-plugin,确实通过execution标签去指定了repackage的goal。

        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <executions>
            <execution>
              <id>repackage</id>
              <goals>
                <goal>repackage</goal>
              </goals>
            </execution>
          </executions>
          <configuration>
            <mainClass>${start-class}</mainClass>
          </configuration>
        </plugin>

这下明白了,默认情况下spring-boot-maven-plugin并不会指定Maven的自动打包,只是定义了repackage这个Mojo,我们通过Intellij可以看到。

为什么导入了SpringBootMaven插件打包结果不是SpringBoot的大Jar包,导致Jar包无法正常启动,第2张

如果我们想要在没有spring-boot-starter-parent的情况下使用,就可以添加上如下的配置,去进行开启。(实际上,没有spring-boot-starter-parent的情况是很常见的,比如在一些大公司当中,公司可能会自定义一些中间件,就可能会要求所有的项目统一导入公司的父POM*,此时就会导致我们无法使用spring-boot-starter-parent去作为父工程,就很可能需要用到手动配置repackage)

                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>

https://www.xamrdz.com/lan/5ug1996082.html

相关文章: