maven部署專案流程(區分環境)
java專案區分環境打包部署到Linux 本文以一個簡單的HelloWorld的專案為例,從pom.xml配置到打jar包,最後在linux上執行並驗證來對maven部署的這套流程進行簡單介紹。 為方便講解,本文使用的時springboot框架,其執行函式入口程式如下:
@SpringBootApplication public class ProjectApplication { private static String env; public static void main(String[] args) { SpringApplication.run(ProjectApplication.class, args); System.out.println("hello world!"); System.out.println("current environment is: " + env); } @Value("${env}") private void setEnv(String v) { env = v; } }
為驗證分環境打包功能,這裡使用你了兩個目錄,分別存放配置檔案,分別時開發環境dev目錄下的中的application.yml,以及生產環境prod目錄下的application.yml dev的yml配置內容:
env: dev server: port: 8081
prod的yml配置內容:
env: prod server: port: 8082
專案目錄:
pom.xml配置過程:
第一步,使用<profiles>標籤
<profiles> <!-- 開發環境 --> <profile> <id>dev</id> <properties> <profile.dir>${profiles.dir}/dev</profile.dir> </properties> <activation> <activeByDefault>true</activeByDefault> <!--將dev設定為預設環境--> </activation> <build> <resources> <resource> <directory>${profile.dir}/dev</directory> <filtering>true</filtering> </resource> </resources> </build> </profile> <!-- 生產環境 --> <profile> <id>prod</id> <properties> <profile.dir>${profiles.dir}/prod</profile.dir> </properties> </profile> </profiles>
注意這裡${profiles.dir}對應:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <profiles.dir>src/main/profiles</profiles.dir> </properties>
如果要給開發環境打包使用如下命令: mvn clean package -Pdev -Dmaven.test.skip=true
如果要給生產環境打包使用如下命令: mvn clean package -Pprod -Dmaven.test.skip=true
第二步,使用<build>標籤
- 引入
maven-jar-plugin
- 引入
maven-assembly-plugin
先說maven-assembly-plugin,顧名思義,就是整合的意思,比如bulid專案之後,會在target目錄下生成很多零碎的檔案,比如jar,config配置,bin目錄等,如果想將這些檔案打包成一個壓縮包就可以使用這個外掛來實現,例如它打包可以為zip,tar.gz,dir等等形式。 一般maven-assembly-plugin以如下方式執行:
<plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <!-- not append assembly id in release file name --> <appendAssemblyId>false</appendAssemblyId> <descriptors> <descriptor>src/main/assembly/assembly.xml</descriptor> </descriptors> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal><!-- 只執行一次 --> </goals> </execution> </executions> </plugin>
然後對應的assembly.xml來處理打包具體細節。 比如:
<assembly> <id>assembly</id> <formats> <format>dir</format> <!-- 打包為檔案 --> <format>tar.gz</format> <!-- 打包為tar.gz --> </formats> <includeBaseDirectory>true</includeBaseDirectory> <fileSets> <fileSet> <!-- 將bin目錄的檔案存放到整合檔案的bin目錄下 --> <directory>src/main/assembly/bin</directory> <outputDirectory>bin</outputDirectory> <fileMode>0755</fileMode> <filtered>true</filtered> </fileSet> </fileSets> <dependencySets> <dependencySet> <!-- 將所有的依賴放到lib目錄下 --> <outputDirectory>lib</outputDirectory> </dependencySet> </dependencySets> </assembly>
這樣使用mvn clean package -Pprod
-Dmaven.test.skip=true打包整合的目錄結構大致如下:
project-0.0.1-SNAPSHOT
├── bin
│ ├── start.sh │ └── stop.sh └── lib ├── javax.annotation-api-1.3.2.jar ├── jul-to-slf4j-1.7.25.jar ├── log4j-api-2.11.1.jar ├── log4j-to-slf4j-2.11.1.jar ├── logback-classic-1.2.3.jar ├── logback-core-1.2.3.jar ├── project-0.0.1-SNAPSHOT.jar ├── slf4j-api-1.7.25.jar ├── snakeyaml-1.23.jar ├── spring-aop-5.1.2.RELEASE.jar ├── spring-beans-5.1.2.RELEASE.jar
只使用maven-assembly-plugin便可以形成tar.gz包,但是執行的時候會出問題:
“no main manifest attribute, in /hom......jar”
這是因為:只使用maven-assembly-plugin打包成jar包需要在MANIFEST.MF中指定沒有指定Main-Class項,程式不知道執行入口。當前MANIFEST.MF資訊如下:
Manifest-Version: 1.0 Implementation-Title: project Implementation-Version: 0.0.1-SNAPSHOT Built-By: youai Implementation-Vendor-Id: cjj.example Created-By: Apache Maven 3.5.0 Build-Jdk: 1.8.0_181 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/project
此時,就要引入maven-jar-plugin來問題解決 maven-jar-plugin主要為配置MANIFEST.MF而生。一個MANIFEST.MF檔案,裡面記錄了可執行檔案的一些相關配置。 因此在pom.xml中的新增內容如下:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>./</classpathPrefix> <mainClass>cjj.example.project.ProjectApplication</mainClass> </manifest> </archive> <excludes> <exclude>*</exclude> </excludes> </configuration> </plugin>
其中:
- mainClass :哪個class作為程式的入口來執行;
- addClasspath: 是否將依賴的classpath一起打包 ;
- classpathPrefix:依賴的classpath的字首,也就是打包後生成的MANIFEST.MF檔案裡,引入的jar檔案都會加上字首, 本專案裡指都加上字首“./”,比如 spring-boot-2.1,在mainfest檔案裡就會是./spring-boot-2.1 。
- excludes:排除哪些檔案不被打包進去。因此在本專案中的lib包中不會存在資料夾。
此時打包出來的目錄結構沒變,只是當前MANIFEST.MF有了變化,資訊如下:
Manifest-Version: 1.0 Implementation-Title: project Implementation-Version: 0.0.1-SNAPSHOT Built-By: youai Implementation-Vendor-Id: cjj.example Class-Path: ./spring-boot-starter-2.1.0.RELEASE.jar ./spring-boot-2.1. 0.RELEASE.jar ./spring-context-5.1.2.RELEASE.jar ./spring-aop-5.1.2.R ELEASE.jar ./spring-beans-5.1.2.RELEASE.jar ./spring-expression-5.1.2 .RELEASE.jar ./spring-boot-autoconfigure-2.1.0.RELEASE.jar ./spring-b oot-starter-logging-2.1.0.RELEASE.jar ./logback-classic-1.2.3.jar ./l ogback-core-1.2.3.jar ./log4j-to-slf4j-2.11.1.jar ./log4j-api-2.11.1. jar ./jul-to-slf4j-1.7.25.jar ./javax.annotation-api-1.3.2.jar ./spri ng-core-5.1.2.RELEASE.jar ./spring-jcl-5.1.2.RELEASE.jar ./snakeyaml- 1.23.jar ./slf4j-api-1.7.25.jar Created-By: Apache Maven 3.5.0 Build-Jdk: 1.8.0_181 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/project Main-Class: cjj.example.project.ProjectApplication
一切準備好後,激動地準備啟動指令碼,也可以執行命令:
java -jar project-0.0.1-SNAPSHOT.jar
執行檔案,會出現如下錯誤:
ith name 'projectApplication': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'env' in value "${env}" 2018-11-30 15:22:16.897 INFO 12528 --- [ main] ConditionEvaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2018-11-30 15:22:16.901 ERROR 12528 --- [ main] o.s.boot.SpringApplication : Application run failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'projectApplication': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'env' in value "${env} "
原因:application.yml檔案並沒有載入進去。
因此需要將配置檔案關聯起來,方式如下: maven-jar-plugin中加入manifestEntries,將application.yml檔案關聯到jar的啟動更目錄下:
<archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>./</classpathPrefix> <mainClass>cjj.example.project.ProjectApplication</mainClass> </manifest> <manifestEntries> <Class-Path>../config/</Class-Path> </manifestEntries> </archive>
重新打包,當前MANIFEST.MF資訊如下:
Manifest-Version: 1.0 Implementation-Title: project Implementation-Version: 0.0.1-SNAPSHOT Built-By: youai Implementation-Vendor-Id: cjj.example Class-Path: ./spring-boot-starter-2.1.0.RELEASE.jar ./spring-boot-2.1. 0.RELEASE.jar ./spring-context-5.1.2.RELEASE.jar ./spring-aop-5.1.2.R ELEASE.jar ./spring-beans-5.1.2.RELEASE.jar ./spring-expression-5.1.2 .RELEASE.jar ./spring-boot-autoconfigure-2.1.0.RELEASE.jar ./spring-b oot-starter-logging-2.1.0.RELEASE.jar ./logback-classic-1.2.3.jar ./l ogback-core-1.2.3.jar ./log4j-to-slf4j-2.11.1.jar ./log4j-api-2.11.1. jar ./jul-to-slf4j-1.7.25.jar ./javax.annotation-api-1.3.2.jar ./spri ng-core-5.1.2.RELEASE.jar ./spring-jcl-5.1.2.RELEASE.jar ./snakeyaml- 1.23.jar ./slf4j-api-1.7.25.jar ../config/ Created-By: Apache Maven 3.5.0 Build-Jdk: 1.8.0_181 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/project Main-Class: cjj.example.project.ProjectApplication
可知在Class-Path: 下加入了 ../config/,因此我們只要將對應環境的application.yml打包時放入config目錄內就可以解決上面的問題,如何放入config可以使用resource標籤。需要在pom.xml的build模組下時,引入resources標籤。
<resources> <resource> <directory>${profile.dir}</directory> <filtering>true</filtering> <targetPath>${project.build.directory}/config</targetPath> </resource> </resources>
同時更新assembly.xml檔案,在 <fileSets>標籤下加入
<fileSet> <directory>${project.build.directory}/config</directory> <outputDirectory>config</outputDirectory> <fileMode>0644</fileMode> </fileSet>
這樣在重新打包後會在生成的target目錄下產生一個conofig目錄,裡面存放的內容為${profile.dir}目錄下的檔案。然後通過assembly外掛可以將config目錄打包到打包壓縮檔案tar.gz中了 如下:
將執行mvn clean package -Pprod -Dmaven.test.skip=true生成的project-0.0.1-SNAPSHOT.tar.gz包上傳到linux伺服器,然後執行start.sh。
執行結果:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.0.RELEASE) 2018-11-30 14:51:28.707 INFO 18601 --- [ main] cjj.example.project.ProjectApplication : Starting ProjectApplication v0.0.1-SNAPSHOT on datahub2 with PID 18601 (/home/dmadmin/cjj/project-0.0.1-SNAPSHOT/lib/project-0.0.1-SNAPSHOT.jar started by dmadmin in /home/dmadmin/cjj/project-0.0.1-SNAPSHOT/bin) 2018-11-30 14:51:28.714 INFO 18601 --- [ main] cjj.example.project.ProjectApplication : No active profile set, falling back to default profiles: default 2018-11-30 14:51:29.776 INFO 18601 --- [ main] cjj.example.project.ProjectApplication : Started ProjectApplication in 1.676 seconds (JVM running for 2.27) hello world! current environment is: prod
注意:在linux環境下執行start.sh,如果star.sh的檔案型別為dos, 可以通過vim start.sh中set fileformat=unix來解決。
參考:
- http://maven.apache.org/plugins/maven-assembly-plugin/
- https://blog.csdn.net/mrluzle/article/details/79164342(SpringBoot 配置檔案存放位置及讀取順序)
- https://www.java-success.com/maven-assembly-plugin-example/ (同時執行多個assembly.xml,如dev-assembly.xml,test-assembly.xml, prod-assembly.xml。goal標籤<goal>shade</goal> )
- https://www.mkyong.com/maven/create-a-fat-jar-file-maven-assembly-plugin/ (Create a fat Jar file – Maven Assembly Plugin)