1. 程式人生 > >將 Spring boot 項目打成可執行Jar包,及相關註意事項(main-class、缺少 xsd、重復打包依賴)

將 Spring boot 項目打成可執行Jar包,及相關註意事項(main-class、缺少 xsd、重復打包依賴)

arc XA 有一個 文件中 pom.xml tomcat 我們 tool pil

最近在看 spring boot 的東西,覺得很方便,很好用。對於一個簡單的REST服務,都不要自己部署Tomcat了,直接在 IDE 裏 run 一個包含 main 函數的主類就可以了。

但是,轉念一想,到了真正需要部署應用的時候,不可能通過 IDE 去部署啊。那有沒有辦法將 spring boot 的項目打包成一個可執行的 jar 包,然後通過 java -jar 命令去啟動相應的服務呢?

很明顯,是有的。下面,我把我自己的實踐過程及遇到的問題,一 一說明一下。

首先,把項目的 POM 配置文件的雛形放上來
PS: (代碼我就不放上來了,spring boot 官網上有。我在本文的最下面會給出鏈接。)

<?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>

    <artifactId>spring-boot</artifactId>
    <version>0.1-SNAPSHOT</version>
    <name>spring-boot</name>

    <packaging>jar</packaging>

    <parent>
        <groupId>org.rainbow</groupId>
        <artifactId>spring</artifactId>
        <version>0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

這裏,我沒有使用 spring boot 默認的 parent 工程,而是使用自己項目的 parent 工程,具體請參見 我的另一篇Blog

只要有了上面的這段 pom 配置,你就可以在 IDE 裏啟動你的應用了。

下面,說明一下,將項目打成 可執行Jar包 所需要的配置。

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>org.rainbow.spring.boot.Application</mainClass>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

很簡單吧?我們只需要添加一個 spring-boot-maven-plugin 插件就可以解決問題了。

加了這個插件之後,我們可以通過下面的方式來將項目打成可執行jar包。

mvn clean package
  • 1

請註意,從我們上面的配置來看,雖然我們沒有明確寫出將插件的 repackage 這個 goal 綁定到了 maven 的哪個 life cycle 上,但是插件本身默認將它綁定到了 maven 的 package 上。

所以,只有當我們執行的 maven 命令會觸發 package 這個life cycle 時,上面的插件才會被觸發。

另外,我們可以在上面的 pom 配置中,去掉下面這段配置:

<executions>
    <execution>
        <goals>
            <goal>repackage</goal>
        </goals>
    </execution>
</executions>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

然後,我們可以通過手動來執行插件的 repackage 這個 goal。

mvn clean package spring-boot:repackage
  • 1

其中,spring-boot 是固定的前綴。

從以上的描述來看,我們一共有兩種方式來啟用這個插件,任選其一哦。

執行了這個插件之後,你會在 target 目錄下發現兩個Jar包:

  • xxxxx.jar.original
  • xxxxx.jar

其中,第一個是僅僅包含我們項目源碼的 Jar包,它是無法運行的。第二個是經由 spring boot maven plugin 重新包裝後的Jar包,這個是可以運行的。可以通過下面的命令來試下:

java -jar xxxxx.jar
  • 1

然後,你應該會看到下面類似的啟動信息:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/XXXXXXX/Desktop/spring-boot-0.1-SNAPSHOT.jar!/BOOT-INF/classes!/org/slf4j/impl/StaticLoggerBinder.class]

SLF4J: Found binding in [jar:file:/C:/Users/XXXXXXX/Desktop/spring-boot-0.1-SNAPSHOT.jar!/BOOT-INF/lib/logback-classic-1.1.9.jar!/org/slf4j/impl/StaicLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

  .   ____          _            __ _ _
 /\\ / ___‘_ __ _ _(_)_ __  __ _ \ \ \ ( ( )\___ | ‘_ | ‘_| | ‘_ \/ _` | \ \ \  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  ‘  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::

2017-02-14 10:47:29.488  INFO 11860 --- [           main] org.rainbow.spring.boot.Application      : Starting Application on XXXXXXX-PC with PID 11860 (C:\Users\XXXXXXX\Desktop\spring-boot-0.1-SNAPSHOT.jar started by XXXXXXX in C:\Users\XXXXXXX\Desktop)
2017-02-14 10:47:29.494  INFO 11860 --- [           main] org.rainbow.spring.boot.Application      : No active profile set, falling back to default profiles: default
2017-02-14 10:47:29.607  INFO 11860 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@14514713: startup date [Tue Feb 14 10:47:29 CST 2017]; root of context hierarchy
2017-02-14 10:47:31.731  INFO 11860 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean ‘org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration‘ of type [class org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-02-14 10:47:31.849  INFO 11860 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean ‘validator‘ of type [class org.springframework.validation.beanvalidation.LocalValidatorFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-02-14 10:47:32.673  INFO 11860 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2017-02-14 10:47:32.699  INFO 11860 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2017-02-14 10:47:32.701  INFO 11860 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.11
2017-02-14 10:47:32.848  INFO 11860 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2017-02-14 10:47:32.848  INFO 11860 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3244 ms
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

下面說明一下幾個註意事項。

第一個是有關於 main 函數的。

我們知道,一個jar包要能夠運行,那麽必須在其根目錄下的 META-INF 目錄下的 MANIFEST.MF 文件中聲明 Main-Class 這個屬性。

對於 spring boot 的項目來說,這一點也是必須的。那麽,我們應該如何來聲明我們項目中的 main函數所在的 class 呢?

方法有二。

1. 不作任何聲明

即,我們不添加任何的其他聲明。這樣一來,spring boot maven plugin 在打包時,會自動掃描整個項目的源碼,並將掃描到的第一個包含 main 函數的 class 作為Jar包的 Main-Class。

2. 在 plugin 的配置中增加一個配置

<configuration>
    <mainClass>org.rainbow.spring.boot.Application</mainClass>
</configuration>    
  • 1
  • 2
  • 3

這樣的話,Application 這個class將作為Jar包的 Main-Class。

但是,你會發現,在最終打好的Jar中, Application 這個class,它並不是作為 Main-Class 這個屬性的值,而是作為 Start-Class 屬性的值。

這個是由 spring boot 自己進行處理的,我們無須過多關註。
(其實,在打好的Jar中,我們去看一下其中的 MANIFEST.MF文件,可以發現,它的 Main-Class 指定的值是 org.springframework.boot.loader.JarLauncher, spring boot 會通過這個類去間接的執行 Start-Class 指定的類,即我們的主類)

第二個問題是關於項目可能會報找不到 spring 的某些 XSD 文件的。
PS:以下篇幅來自 Spring如何加載XSD文件


說明:
這個問題,我在自己的項目中沒有遇到,但是在網上看到這個問題的描述及處理。為了防止項目以後遇到問題,我就在此一起列出來。

Start.

問題現象是:

org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document ‘http://www.springframework.org/schema/beans/spring-beans-3.0.xsd‘, because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.  
  • 1

很顯然,spring xml配置文件中指定的xsd文件讀取不到了,原因多是因為斷網或spring的官網暫時無法連接導致的。 你可以通過在瀏覽器輸入xsd文件的URL,如:http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 進行確認。

為什麽會這樣呢?要想直正弄明白這一問題還需要從spring的XSD文件加載機制談起。

你必須知道一點:spring在加載xsd文件時總是先試圖在本地查找xsd文件(spring的jar包中已經包含了所有版本的xsd文件),如果沒有找到,才會轉向去URL指定的路徑下載。

這是非常合理的做法,並不像看上去的那樣,每次都是從站點下載的。

事實上,假如你的所有配置是正確定的,你的工程完全可以在斷網的情況下啟動而不會報上面的錯誤。Spring加載xsd文件的類是PluggableSchemaResolver,你可以查看一下它的源碼來驗證上述說法。

另外,你可以在log4j.xml文件中加入:

<logger name="org.springframework.beans.factory.xml">  
    <level value="all" />  
</logger> 
  • 1
  • 2
  • 3

通過日誌了解spring是何加載xsd文件的。

接下來,問題就是為什麽spring在本地沒有找到需要的文件,不得不轉向網站下載。關於這個問題,其實也非常簡單:

在很多spring的jar包裏,在META-INF目錄下都有一個spring.schemas,這是一個property文件,其內容類似於下面:

http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd  
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd  
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd  
....  
  • 1
  • 2
  • 3
  • 4

實際上,這個文件就是spring關於xsd文件在本地存放路徑的映射,spring就是通過這個文件在本地(也就是spring的jar裏)查找xsd文件的。

那麽,查找不到的原因排除URL輸入有誤之外,可能就是聲明的xsd文件版本在本地不存在。

一般來說,新版本的spring jar包會將過去所有版本(應該是自2.0以後)的xsd打包,並在spring.schemas文件中加入了對應項,出現問題的情況往往是聲明使用了一個高版本的xsd文件,如3.0,但依賴的spring的jar包卻是2.5之前的版本,由於2.5版本自然不可能包含3.0的xsd文件,此時就會導致spring去站點下載目標xsd文件,如遇斷網或是目標站點不可用,上述問題就發生了。

但是,在實現開發中,出現上述錯誤的幾率並不高,最常見的導致這一問題的原因其實與使用了一個名為“assembly”的maven打包插件有關。

很多項目需要將工程連同其所依賴的所有jar包打包成一個jar包,maven的assembly插件就是用來完成這個任務的。但是由於工程往往依賴很多的jar包,而被依賴的jar又會依賴其他的jar包,這樣,當工程中依賴到不同的版本的spring時,在使用assembly進行打包時,只能將某一個版本jar包下的spring.schemas文件放入最終打出的jar包裏,這就有可能遺漏了一些版本的xsd的本地映射,進而出現了文章開始提到的錯誤。

如果你的項目是打成單一jar的,你可以通過檢查最終生成的jar裏的spring.schemas文件來確認是不是這種情況。

而關於這種情況,解決的方法一般是推薦使用另外一種打包插件”shade“,它確實是一款比assembly更加優秀的工具,在對spring.schemas文件處理上,shade能夠將所有jar裏的spring.schemas文件進行合並,在最終生成的單一jar包裏,spring.schemas包含了所有出現過的版本的集合!

以上就是spring加載XSD文件的機制和出現問題的原因分析。實際上,我們應該讓我們工程在啟動時總是加載本地的xsd文件,而不是每次去站點下載,做到這一點就需要你結合上述提及的種種情況對你的工程進行一番檢查。

End.

好了,到此,我們了解了這個問題,並且知道了可以使用哪個插件來避免這個問題。那麽,下面我們就說一下上面提及到的 shade 插件如何配置吧。

我先直接將配置發上來吧:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <createDependencyReducedPom>true</createDependencyReducedPom>
                <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
                <minimizeJar>false</minimizeJar>
                <promoteTransitiveDependencies>false</promoteTransitiveDependencies>

                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.factories</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.handlers</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.provides</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.schemas</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.tooling</resource>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

從上面的配置來看,這個插件也是在 maven 的 package 階段才會被觸發,與上面介紹的 spring boot maven plugin 是一樣的。

下面重點說明一下 transformer 這個標簽的作用。

上面雖然寫了 5 個 transformer,但其實都一樣,只不過是處理了5個不同的文件而已:

  • META-INF/spring.factories
  • META-INF/spring.handlers
  • META-INF/spring.provides
  • META-INF/spring.schemas
  • META-INF/spring.tooling

下面 以 META-INF/spring.factories 為例進行說明。

上面的配置就是將所有被項目依賴的Jar包中的 META-INF/spring.factories 文件合並到一份文件中,這份文件將作為最終的 Jar包 中的 META-INF/spring.factories 這個文件。(名稱並沒有發生變化)。

其實,這個插件還有一個 ManifestResourceTransformer,我們可以通過這個 transformer 來設定 Jar 的Main-Class 等屬性,如下:

<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
  <manifestEntries>
    <Main-Class>${app.main.class}</Main-Class>
    <X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK>
    <X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK>
  </manifestEntries>
</transformer>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

這裏列出來的屬性,都將被寫入到 META-INF/MANIFEST.MF 文件中。

不過,需要註意的一點是:雖然可以通過此 transformer 來設定 Jar包的 Main-Class,但是此處設定的值將會被在spring boot maven plugin 設定的 Main-Class 的值所替代掉。因為 spring boot maven plugin 插件是在 apache maven shade plugin 之後執行的。

第三個問題,是關於項目重復引入依賴包的問題。


2017.3.18 補充:
經過最近的測試,我個人覺得,只需要使用 spring-boot-maven 這個插件就可以了。因為這個插件會將所有依賴的 jar 打到最終的jar裏去,並不會發生上面問題二中所說的: xld 中元素變少的情況。
而這第三個問題,就是由於上面使用了 shade 插件導致的。所以,如果你只使用了 spring-boot-maven 的插件的話,問題二 和 問題三 都無視吧。。。

如果細心的話,我們會發現上面有這麽一段輸出:

SLF4J: Class path contains multiple SLF4J bindings.

SLF4J: Found binding in [jar:file:/C:/Users/XXXXXXX/Desktop/spring-boot-0.1-SNAPSHOT.jar!/BOOT-INF/classes!/org/slf4j/impl/StaticLoggerBinder.class]

SLF4J: Found binding in [jar:file:/C:/Users/XXXXXXX/Desktop/spring-boot-0.1-SNAPSHOT.jar!/BOOT-INF/lib/logback-classic-1.1.9.jar!/org/slf4j/impl/StaicLoggerBinder.class]

SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.

SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

意思就是說,在 classpath 中發現了兩個 SLF4J 的綁定:

  • BOOT-INF/classes!/org/slf4j/impl/StaticLoggerBinder.class
  • BOOT-INF/lib/logback-classic-1.1.9.jar!/org/slf4j/impl/StaicLoggerBinder.class

這麽看來,應該是 StaticLoggerBinder.class 被引入了兩次。下面,我們看一下經過 spring boot maven 插件打包好的jar包,在解壓之後的文件夾結構是怎麽樣的。請看:

技術分享圖片

我們看到,BOOT-INF 目錄下的 class 和 lib 目錄下,幾乎所有的依賴都被分別導入了一份。那這個結構的是怎麽來的呢?大概下面這樣的:

  • 整個打包過程,是先執行 maven shade 插件,將項目依賴的所有jar的class文件抽取出來做成一個 fat jar,它生的jar包的結構(假設名為 1.jar, 該名稱下面會使用到),大概如下圖所示:

技術分享圖片

  • 然後,再執行 spring boot maven 插件(為方便描述,假設該步驟生成的jar包名稱為 2.jar),將上面生成的 1.jar 中的 META-INF 文件夾作為 2.jar 的 META-INF,1.jar 中的其他文件,全部移至 2.jar 中的 BOOT-INF/class 文件夾下
  • 同時,spring boot maven 插件會將項目所有依賴的所有jar包,再次打包進 2.jar 的 BOOT-INF/lib 下。
  • 另外,spring boot 會將 spring boot loader 的 class 文件放至 2.jar 的根目錄下(上上個圖的中最後一個名為org的目錄),用於啟動jar包。

好了,既然現在知道問題發生在哪裏了,那就想辦法去掉其中的一個唄?那該如何去掉呢?我經過一些調查與測試之後發現,只能在 shade 插件中增加相關配置來過濾掉 class 目錄下的重復的類。原因有以下幾點:

  • 我們需要使用 maven shade 插件來避免上面說到的 xsd 的問題
  • spring boot maven 插件雖然提供了 excludeArtifactIds、 excludeGroupIds 和 excludes 屬性來配置需要排除的 依賴,但是它只能完全匹配,不能使用 * 或者 ? 這兩個通配符進行模糊匹配,所以這幾個屬性只適用於要排除個別依賴的情景。

下面,我們來看下如何配置 maven shade 的插件來避免重復引用依賴的問題:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <createDependencyReducedPom>true</createDependencyReducedPom>
                <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
                <minimizeJar>false</minimizeJar>
                <promoteTransitiveDependencies>false</promoteTransitiveDependencies>

                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.factories</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.handlers</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.provides</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.schemas</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.tooling</resource>
                    </transformer>
                </transformers>

                <!-- use filter to include only the needed files -->
                <filters>
                    <filter>
                        <artifact>*:*</artifact>
                        <includes>
                            <include>*</include>
                            <include>META-INF/**</include>
                            <include>org/rainbow/**</include>
                        </includes>
                    </filter>
                </filters>
            </configuration>
        </execution>
    </executions>
</plugin>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

重點是最後面的 filter 屬性的配置。我這麽配置的作用是:

  • 保留jar包根目錄下的所有文件
  • 保留jar包META-INF目錄及其子目錄下的所有文件
  • 保留jar包org/rainbow/目錄及其子目錄下的所有文件 (org/rainbow/ 是我自己寫的代碼的package的前綴,通過該規則來保留我自己的源碼)
  • 對所有依賴,執行上面三個過濾

當然,對於某些特殊的jar包,上面的這個規則列表可能還不完善,需要根據實際情況進行修改。

最後,給出項目的完整 POM 配置:

<?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>

    <artifactId>spring-boot</artifactId>
    <version>0.1-SNAPSHOT</version>
    <name>spring-boot</name>

    <packaging>jar</packaging>

    <parent>
        <groupId>org.rainbow</groupId>
        <artifactId>spring</artifactId>
        <version>0.1-SNAPSHOT</version>
    </parent>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-loader</artifactId>
            <version>1.5.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>logback-classic</artifactId>
                    <groupId>ch.qos.logback</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.7</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <createDependencyReducedPom>true</createDependencyReducedPom>
                            <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
                            <minimizeJar>false</minimizeJar>
                            <promoteTransitiveDependencies>false</promoteTransitiveDependencies>

                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.factories</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.handlers</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.provides</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.schemas</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.tooling</resource>
                                </transformer>
                            </transformers>

                            <!-- use filter to include only the needed files -->
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <includes>
                                        <include>*</include>
                                        <include>META-INF/**</include>
                                        <include>org/rainbow/**</include>
                                    </includes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>org.rainbow.spring.boot.Application</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129

參考文檔

  • spring boot quick start
  • Developing your first Spring Boot application
  • Spring Boot Maven plugin Demo
  • Spring Boot Maven Plugin Doc
  • Apache Maven Shade Plugin
  • Apache Maven Shade Plugin - Transformers
  • Spring如何加載XSD文件(org.xml.sax.SAXParseException: Failed to read schema document錯誤的解決方法)
  • 使用maven插件對java工程進行打包
版權聲明:本文為博主原創文章,歡迎轉載,但是必須在文章的開頭聲明出處! https://blog.csdn.net/rainbow702/article/details/55096506

將 Spring boot 項目打成可執行Jar包,及相關註意事項(main-class、缺少 xsd、重復打包依賴)