Maven是Java应用中一种很常见的构建方式,这里记录下我们使用Maven来构建springboot应用的一次实践以及遇到的问题
实践 在项目实施过程中,一般都会将一些通用的处理提取成公共的依赖,形成所谓的框架。这里我们参考springboot的方式也整理了自己的一套commons包,涵盖了项目中常见的各种问题处理,希望能应用到公司的各个项目中,并且尽可能不需要做任何的改动
commons-dependencies: 统一管理依赖的版本定义,使用dependencyManagement来定义依赖,这样约定我们所有的服务模块都使用相同的依赖版本;
commons-framework: 抽象出来的公共处理,不依赖于具体的项目业务,在spring的基础上做了一些偏于项目的公共处理,但是又与具体的业务模块没有直接关系;
commons-parent :在上面两个依赖的基础上,进一步对各种plugin和配置做了统一的定义;
如果基于commons包来构建项目工程,其依赖关系可以如下图所示
最底层还是springboot等那些常见的依赖,最上层是我们的服务应用,commons依赖只是在中间做了一些约定和配置,以及一些公共的抽象处理
commons-dependencies commons-dependencies/ pom.xml 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 <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.xxx</groupId > <artifactId > commons-dependencies</artifactId > <version > 1.0.0</version > <packaging > pom</packaging > <name > commons-dependencies</name > <url > https://www.xxx.com/</url > <description > 依赖管理</description > <properties > <spring.boot.version > 2.7.0</spring.boot.version > <grpc.version > 1.48.0</grpc.version > <commons.io.version > 2.11.0</commons.io.version > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > ${spring.boot.version}</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > io.grpc</groupId > <artifactId > grpc-bom</artifactId > <version > ${grpc.version}</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > commons-io</groupId > <artifactId > commons-io</artifactId > <version > ${commons.io.version}</version > </dependency > </dependencies > </dependencyManagement > </project >
commons-framework commons-framework/ pom.xml 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 <?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.xxx</groupId > <artifactId > commons-framework</artifactId > <version > 1.0.0</version > <packaging > jar</packaging > <name > commons-framework</name > <url > https://www.xxx.com/</url > <description > 公共依赖</description > <properties > <maven.compiler.source > 17</maven.compiler.source > <maven.compiler.target > 17</maven.compiler.target > <maven.compiler.encoding > UTF-8</maven.compiler.encoding > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <project.reporting.outputEncoding > UTF-8</project.reporting.outputEncoding > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > com.cowave</groupId > <artifactId > commons-dependencies</artifactId > <version > 1.0.0</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.kafka</groupId > <artifactId > spring-kafka</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency > </dependencies > </project >
scope作用域 其实除了下面这些,我们还希望有一种作用域,比如对于lombol,能够让它只参与编译但不参与运行,并且能传递依赖。最后我们的解决办法是在framework中将lombok的作用域定义为compile,然后在打包插件中再通过配置将这个依赖剔除掉。
compile 默认的scope,适用于所有阶段。最终会打包到artifact中,如果构建的是一个WAR类型artifact,那么compile引用的Jar会被集成到WAR文件中。
provided 适用于编译和测试阶段。这种在类似commons-framework
这样的公用模块中比较常用,它不会打包到artifact中,也不会传递依赖关系。其含义是认为对应的依赖会由运行这个应用的JDK或者容器提供。
runtime 适用于运行和测试阶段。与compile相比,依赖的项目无需参与编译。比如Jdbc的驱动包,项目编译只需要JDK提供的JDBC接口,只有在执行测试或者运行时才需要具体的Jdbc驱动。
test 仅适用于测试阶段。不参与编译和打包,只在编译或运行测试代码的时候才使用,比如Junit。
system 类似于provided,区别在于你需要告诉Maven如何去找到这个依赖,所以当引用的依赖在Maven仓库中不存在时,可以使用,但是不推荐。
pom.xml 1 2 3 4 5 6 7 <dependency > <groupId > org.xxx</groupId > <artifactId > test</artifactId > <version > 1.18.12</version > <scope > system</scope > <systemPath > ${project.basedir}/lib/test.jar</systemPath > </dependency >
如果希望依赖参与打包,spring-boot-maven-plugin需要额外指定,而且打包时不会检查其依赖
pom.xml 1 2 3 4 5 6 7 8 9 10 11 <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <includeSystemScope > true</includeSystemScope > </configuration > </plugin > </plugins > </build >
import 只能在dependencyManagement中使用,用于从其它的pom文件中导入其所有依赖设置,比如对commons-dependencies
的引用
commons-parent commons-parent依赖了framework,并使用commons-dependencies限制依赖版本,另外提供了对properties和plugin的一些定义
commons-parent/ pom.xml 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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 <?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.xxx</groupId > <artifactId > commons-parent</artifactId > <version > 1.0.0</version > <packaging > pom</packaging > <name > commons-parent</name > <url > https://www.xxx.com/</url > <description > 公共parent</description > <properties > <maven.compiler.source > 17</maven.compiler.source > <maven.compiler.target > 17</maven.compiler.target > <maven.compiler.encoding > UTF-8</maven.compiler.encoding > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <project.reporting.outputEncoding > UTF-8</project.reporting.outputEncoding > <plugin.classFinal.phase > none</plugin.classFinal.phase > </properties > <build > <resources > <resource > <directory > src/main/java</directory > <includes > <include > **/*.*</include > </includes > <excludes > <exclude > **/*.java</exclude > </excludes > </resource > <resource > <directory > src/main/resources</directory > </resource > </resources > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.1</version > <configuration > <source > 17</source > <target > 17</target > <encoding > UTF-8</encoding > <compilerArguments > <extdirs > lib</extdirs > </compilerArguments > </configuration > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-jar-plugin</artifactId > <version > 3.1.0</version > <executions > <execution > <id > default-jar</id > <phase > package</phase > <goals > <goal > jar</goal > </goals > <configuration > <finalName > ${project.name}-${project.version}</finalName > <classesDirectory > ${project.build.directory}/classes</classesDirectory > <excludes > <exclude > config/**</exclude > <exclude > smart-doc.json</exclude > <exclude > **/*.java</exclude > </excludes > </configuration > </execution > </executions > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-pmd-plugin</artifactId > <version > 3.20.0</version > <configuration > <targetJdk > ${maven.compiler.target}</targetJdk > <failurePriority > 2</failurePriority > <failOnViolation > true</failOnViolation > <printFailingErrors > true</printFailingErrors > <verbose > true</verbose > <rulesets > <ruleset > rulesets/java/ali-comment.xml</ruleset > <ruleset > rulesets/java/ali-concurrent.xml</ruleset > <ruleset > rulesets/java/ali-constant.xml</ruleset > <ruleset > rulesets/java/ali-exception.xml</ruleset > <ruleset > rulesets/java/ali-flowcontrol.xml</ruleset > <ruleset > rulesets/java/ali-naming.xml</ruleset > <ruleset > rulesets/java/ali-oop.xml</ruleset > <ruleset > rulesets/java/ali-orm.xml</ruleset > <ruleset > rulesets/java/ali-other.xml</ruleset > <ruleset > rulesets/java/ali-set.xml</ruleset > </rulesets > </configuration > <executions > <execution > <id > pmd-check</id > <phase > validate</phase > <goals > <goal > check</goal > </goals > </execution > </executions > <dependencies > <dependency > <groupId > com.alibaba.p3c</groupId > <artifactId > p3c-pmd</artifactId > <version > 2.0.0</version > </dependency > </dependencies > </plugin > <plugin > <groupId > com.github.shalousun</groupId > <artifactId > smart-doc-maven-plugin</artifactId > <version > 2.4.7</version > <executions > <execution > <phase > compile</phase > <goals > <goal > html</goal > </goals > </execution > </executions > <configuration > <configFile > ./src/main/resources/smart-doc.json</configFile > </configuration > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-release-plugin</artifactId > <configuration > <useReleaseProfile > false</useReleaseProfile > <arguments > -Dmaven.javadoc.skip=true</arguments > </configuration > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-scm-plugin</artifactId > <version > 1.13.0</version > <configuration > <connectionType > developerConnection</connectionType > </configuration > </plugin > <plugin > <groupId > net.roseboy</groupId > <artifactId > classfinal-maven-plugin</artifactId > <version > 1.2.1</version > <configuration > <password > #</password > <packages > com.cowave</packages > <libjars > ${project.name}-${project.version}.jar,commons-framework-*.jar</libjars > </configuration > <executions > <execution > <phase > ${plugin.classFinal.phase}</phase > <goals > <goal > classFinal</goal > </goals > </execution > </executions > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-site-plugin</artifactId > <version > 3.12.1</version > </plugin > </plugins > <pluginManagement > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > 2.7.0</version > <executions > <execution > <goals > <goal > repackage</goal > </goals > </execution > </executions > <configuration > <excludes > <exclude > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </exclude > <exclude > <groupId > com.alibaba.p3c</groupId > <artifactId > p3c-pmd</artifactId > </exclude > </excludes > </configuration > </plugin > </plugins > </pluginManagement > </build > <profiles > <profile > <id > unix</id > <activation > <os > <family > unix</family > </os > </activation > <build > <pluginManagement > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-antrun-plugin</artifactId > <version > 1.8</version > <executions > <execution > <id > antrun-build</id > <phase > generate-sources</phase > <goals > <goal > run</goal > </goals > <configuration > <tasks > <chmod file ="${basedir}/bin/*.sh" perm ="ugo+rx" /> </tasks > </configuration > </execution > </executions > </plugin > <plugin > <artifactId > exec-maven-plugin</artifactId > <groupId > org.codehaus.mojo</groupId > <version > 3.0.0</version > <executions > <execution > <id > exec-sources</id > <phase > compile</phase > <goals > <goal > exec</goal > </goals > <configuration > <executable > ${basedir}/bin/install.sh</executable > <arguments > <argument > sources</argument > </arguments > </configuration > </execution > <execution > <id > exec-build</id > <phase > install</phase > <goals > <goal > exec</goal > </goals > <configuration > <executable > ${basedir}/bin/install.sh</executable > <arguments > <argument > build</argument > </arguments > </configuration > </execution > </executions > <configuration > <workingDirectory > ${basedir}</workingDirectory > </configuration > </plugin > </plugins > </pluginManagement > </build > </profile > </profiles > <reporting > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-pmd-plugin</artifactId > <version > 3.20.0</version > <configuration > <targetJdk > ${maven.compiler.target}</targetJdk > <rulesets > <ruleset > rulesets/java/ali-comment.xml</ruleset > <ruleset > rulesets/java/ali-concurrent.xml</ruleset > <ruleset > rulesets/java/ali-constant.xml</ruleset > <ruleset > rulesets/java/ali-exception.xml</ruleset > <ruleset > rulesets/java/ali-flowcontrol.xml</ruleset > <ruleset > rulesets/java/ali-naming.xml</ruleset > <ruleset > rulesets/java/ali-oop.xml</ruleset > <ruleset > rulesets/java/ali-orm.xml</ruleset > <ruleset > rulesets/java/ali-other.xml</ruleset > <ruleset > rulesets/java/ali-set.xml</ruleset > </rulesets > </configuration > </plugin > </plugins > </reporting > <dependencyManagement > <dependencies > <dependency > <groupId > com.cowave</groupId > <artifactId > commons-dependencies</artifactId > <version > 1.0.0</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependencies > <dependency > <groupId > com.cowave</groupId > <artifactId > commons-framework</artifactId > <version > 1.0.0</version > </dependency > </dependencies > </project >
plugin插件 parent主要的作用就是定义好各种要用的plugin,这样具体服务的pom.xml文件就不至于太臃肿,对于那些可选的插件可以定义在pluginManagement中
maven-compiler-plugin compile是默认的操作,通过插件可以指定一些配置,比如<extdirs>
表示可以额外从指定目录中获取仓库之外的依赖
maven-jar-plugin jar也是默认的操作,通过插件可以配置jar包中包含或排除哪些内容,这里将config目录放在jar包外,方便运行时修改配置文件,另外也是希望与以前的项目保持同样的结构
spring-boot-maven-plugin springboot提供的打包方式,将所有文件打包做成一个可以直接java -jar xxx.jar
运行的包,这里配置排除了一些运行时不需要的jar
之前也用过maven-assembly-plugin来将所有文件打成一个tar包,但是在替换环境jar包调试时(坏习惯,以前常用的操作),经常会因为依赖的jar没有替换而导致问题,因为jar包只包含当前编译的代码。如果使用springboot打包就没有这种问题,它会将依赖打包成一个整体。另外,它还有对应的插件方便处理jar包加密问题。如果使用assembly打包,可以如下配置:
pom.xml 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 <properties > <systime > ${maven.build.timestamp}</systime > <maven.build.timestamp.format > yyyyMMdd</maven.build.timestamp.format > <basedirectory > ${project.name}_${project.version}</basedirectory > </properties > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-assembly-plugin</artifactId > <version > 2.2.1</version > <configuration > <finalName > ${project.name}_${project.version}_${systime}</finalName > <appendAssemblyId > false</appendAssemblyId > <descriptors > <descriptor > assembly.xml</descriptor > </descriptors > </configuration > <executions > <execution > <id > make-assembly</id > <phase > package</phase > <goals > <goal > single</goal > </goals > </execution > </executions > </plugin >
对应的还要编写一个打包描述文件,意思就是选取需要打包的文件并放到目标目录,这里将bin下面的install.sh挪到了根目录下
assembly.xml 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 <assembly > <id > assembly-common</id > <includeBaseDirectory > false</includeBaseDirectory > <formats > <format > tar.gz</format > </formats > <dependencySets > <dependencySet > <useProjectArtifact > false</useProjectArtifact > <outputDirectory > ${basedirectory}/lib</outputDirectory > </dependencySet > </dependencySets > <fileSets > <fileSet > <directory > ${project.build.directory}/classes</directory > <excludes > <exclude > com/**</exclude > <exclude > META-INF/**</exclude > </excludes > <outputDirectory > ${basedirectory}</outputDirectory > </fileSet > <fileSet > <directory > lib</directory > <outputDirectory > ${basedirectory}/lib</outputDirectory > </fileSet > <fileSet > <directory > bin</directory > <outputDirectory > ${basedirectory}/bin</outputDirectory > <excludes > <exclude > install.sh</exclude > </excludes > </fileSet > </fileSets > <files > <file > <source > ${project.build.directory}/${project.name}-${project.version}.jar</source > <outputDirectory > ${basedirectory}/lib</outputDirectory > </file > <file > <source > ${basedir}/bin/install.sh</source > <outputDirectory > ${basedirectory}</outputDirectory > </file > </files > </assembly >
classfinal-maven-plugin 针对spring-boot-maven-plugin打出来的jar进行加密,目的是防止反编译。这里将执行阶段phase的值设置成了变量${plugin.classFinal.phase}
,并且默认值为none,表示关闭不执行。然后子模块只要在properties中将值设置为package就可以触发加密了,但是相应的在启动参数中也需要添加 -javaagent
参数
maven-release-plugin/maven-scm-plugin 打包版本自动化控制的一个插件,分为快照版本和发行版本,所以scm要配两个仓库。按照规范,本地开发版本永远是snapshots快照,在发布一个releases之后,它会帮忙将代码一个tag并推送仓库,然后将本地升级到下一个快照
在jenkins中构建时,我们会添加一个参数来表示是否要发布版本
1 2 3 4 5 6 7 if [ "$release_version" = "Yes" ]; then mvn release:clean mvn release:prepare -B mvn release:perform -B cd target/checkout fi mvn clean install
如果这样需要指定拉取的本地分支,因为jenkin拉取代码时会分离Head,这会导致插件push代码失败
smart-doc-maven-plugin smart文档在maven中的插件,根据代码注释生成应用接口文档
exec-maven-plugin/maven-antrun-plugin 在maven构建过程中也可以插入一些自己的操作,这里antrun作用是给脚本添加可执行权限,exec是执行目标脚本。但是如果在window上执行构建,shell脚本会报错,所以可以通过profiles
来进行条件控制,只有操作系统是unix系列时,才触发执行。
在compile阶段执行source()
准备资源,记录应用打包的版本和时间,以及对应的提交作者和版本时间,方便运行后在spring-boot-admin上获取展示; 在install阶段执行build()
手动打包,首先maven-jar-plugin会将编译好的class打包成jar,接着spring-boot-maven-plugin会合并依赖的lib打一个可允许的jar,然后classfinal-maven-plugin对打好的jar再进行加密处理,这里是在前面的基础上打一个tar包,按照期望的目录的结构:bin、config、lib、log
bin/ install.sh 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 sources(){ #git fetch --all #git reset --hard origin/master buildTime=`date "+%Y-%m-%d %H:%M:%S"` appName=`grep -B 4 packaging pom.xml | grep artifactId | awk -F ">" '{print $2}' | awk -F "<" '{print $1}'` appVersion=`grep -B 4 packaging pom.xml | grep version | awk -F ">" '{print $2}' | awk -F "<" '{print $1}'` #commit=`svn info|awk 'NR==9{print $4}'` commit=`git log -n 1 --pretty=oneline | awk '{print $1}'` branch=`git name-rev --name-only HEAD` codeVersion="$branch $commit" commit_msg=`git log --pretty=format:"%s" $s -1` commit_time=`git log --pretty=format:"%cd" $s -1` commit_author=`git log --pretty=format:"%an" $s -1` if [ -f target/classes/META-INF/info.yml ];then ## info.application replace target/classes/META-INF/info.yml name "$appName" 1 replace target/classes/META-INF/info.yml version "$appVersion" 1 replace target/classes/META-INF/info.yml build "$buildTime" 1 ## info.commit replace target/classes/META-INF/info.yml version \""$codeVersion"\" 2 replace target/classes/META-INF/info.yml Msg \""$commit_msg"\" 1 replace target/classes/META-INF/info.yml Time "$commit_time" 1 replace target/classes/META-INF/info.yml Author "$commit_author" 1 ## spring.application.name replace target/classes/META-INF/info.yml name "$appName" 2 fi } build(){ appName=`grep -B 4 packaging pom.xml | grep artifactId | awk -F ">" '{print $2}' | awk -F "<" '{print $1}'` appVersion=`grep -B 4 packaging pom.xml | grep version | awk -F ">" '{print $2}' | awk -F "<" '{print $1}'` buildTime=`date "+%Y-%m-%d %H:%M:%S"` commit=`git log -n 1 --pretty=oneline | awk '{print $1}'` branch=`git name-rev --name-only HEAD` codeVersion="$branch $commit" mkdir -p target/"$appName"_"$appVersion"/lib cp -rf bin target/"$appName"_"$appVersion" mv target/"$appName"_"$appVersion"/bin/install.sh target/"$appName"_"$appVersion" cd target if [ -f "$appName"-"$appVersion"-encrypted.jar ];then cp "$appName"-"$appVersion"-encrypted.jar "$appName"_"$appVersion"/lib/"$appName"-"$appVersion".jar else cp "$appName"-"$appVersion".jar "$appName"_"$appVersion"/lib/"$appName"-"$appVersion".jar fi cp -rf classes/config "$appName"_"$appVersion" find "$appName"_"$appVersion" -type f -name "*.sh" -exec chmod 744 {} \; find "$appName"_"$appVersion" -type f -name "*.sh" -exec dos2unix {} \; sed -i 's#^app_name=.*#app_name="'"$appName"'"#' "$appName"_"$appVersion"/bin/setenv.sh sed -i 's#^app_version=.*#app_version="'"$appVersion"'"#' "$appName"_"$appVersion"/bin/setenv.sh sed -i 's#^code_version=.*#code_version="'"$codeVersion"'"#' "$appName"_"$appVersion"/bin/setenv.sh sed -i 's#^build_time=.*#build_time="'"$buildTime"'"#' "$appName"_"$appVersion"/bin/setenv.sh build_time=`date "+%Y%m%d"` build_tar="$appName"_"$appVersion"_"$build_time".tar.gz tar zcvf $build_tar "$appName"_"$appVersion" md5sum $build_tar > $build_tar.md5 }
如果使用上面的结果,可以很方便按照同样的结构来打一个Docker镜像
docker.build 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 mvn clean install -DskipTests if [ ! $? == 0 ];then exit fi app_name=`grep -B 4 packaging pom.xml | grep artifactId | awk -F ">" '{print $2}' | awk -F "<" '{print $1}'` app_version=`grep -B 4 packaging pom.xml | grep version | awk -F ">" '{print $2}' | awk -F "<" '{print $1}'` cd target app_source="$app_name"_"$app_version" find $app_source -type f -name "*.sh" -exec chmod 744 {} \; find $app_source -type f -name "*.sh" -exec dos2unix {} \; echo "FROM openjdk:17-oracle" >> Dockerfile echo >> Dockerfile echo "WORKDIR /opt/xxx/$app_name" >> Dockerfile echo >> Dockerfile echo "ADD $app_source/bin /opt/xxx/$app_name/bin/" >> Dockerfile echo "ADD $app_source/lib /opt/xxx/$app_name/lib/" >> Dockerfile echo "ADD $app_source/config /opt/xxx/$app_name/config/" >> Dockerfile echo >> Dockerfile echo "ENTRYPOINT [\"/opt/xxx/$app_name/bin/run.sh\", \"up\"]" >> Dockerfile docker build -t xxx/$app_name:$app_version .
maven-pmd-plugin 代码静态检查插件,这里我们使用的alibaba的规范,在compile会先扫描代码,如果有级别高于2的问题,则拒绝编译
maven-site-plugin/reporting 用于生成Maven应用相关的Html文件,可以将pmd检查报告添加到网页中,这样相比pmd.xml可以有更好的浏览体验
xxx-model 对于多个微服务构成的项目,我们会先将这些服务中共用的数据模型抽象出来作为一个独立的依赖包,这样当服务相互调用进行序列化或反序列化时,就不需要重复进行类型定义了。至于它的pom.xml则可以非常简单
xxx-model/ pom.xml 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 <?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 > <parent > <groupId > com.xxx</groupId > <artifactId > commons-parent</artifactId > <version > 1.0.0</version > </parent > <artifactId > xxx-model</artifactId > <version > 1.0.0-SNAPSHOT</version > <packaging > jar</packaging > <name > xxx-model</name > <url > https://www.xxx.com/</url > <description > xxx-模型管理</description > <scm > <tag > HEAD</tag > <url > http://192.168.141.13:8083/xxx/xxx-model</url > <connection > scm:git:http://192.168.141.13:8083/xxx/xxx-model.git</connection > <developerConnection > scm:git:http://192.168.141.13:8083/xxx/xxx-model.git</developerConnection > </scm > <distributionManagement > <repository > <id > xxx</id > <name > releases</name > <url > http://192.168.141.13:8081/repository/maven-releases/</url > <uniqueVersion > true</uniqueVersion > </repository > <snapshotRepository > <id > xxx</id > <name > snapshots</name > <url > http://192.168.141.13:8081/repository/maven-snapshots/</url > <uniqueVersion > true</uniqueVersion > </snapshotRepository > </distributionManagement > </project >
xxx-service 对于具体的项目服务,其pom文件同样很简单,相比model只是声明了三个打包插件,而插件的配置已经在parent中定义好了
xxx-serviceA/ pom.xml 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 <?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 > <parent > <groupId > com.xxx</groupId > <artifactId > commons-parent</artifactId > <version > 1.0.0</version > </parent > <artifactId > xxx-serviceA</artifactId > <version > 1.0.0-SNAPSHOT</version > <packaging > jar</packaging > <name > xxx-serviceA</name > <url > https://www.xxx.com/</url > <description > xxx-服务A</description > <scm > <tag > HEAD</tag > <url > http://192.168.141.13:8083/xxx/xxx-serviceA</url > <connection > scm:git:http://192.168.141.13:8083/xxx/xxx-serviceA.git</connection > <developerConnection > scm:git:http://192.168.141.13:8083/xxx/xxx-serviceA.git</developerConnection > </scm > <distributionManagement > <repository > <id > xxx</id > <name > releases</name > <url > http://192.168.141.13:8081/repository/maven-releases/</url > <uniqueVersion > true</uniqueVersion > </repository > <snapshotRepository > <id > xxx</id > <name > snapshots</name > <url > http://192.168.141.13:8081/repository/maven-snapshots/</url > <uniqueVersion > true</uniqueVersion > </snapshotRepository > </distributionManagement > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > <profiles > <profile > <id > unix</id > <activation > <os > <family > unix</family > </os > </activation > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-antrun-plugin</artifactId > </plugin > <plugin > <artifactId > exec-maven-plugin</artifactId > <groupId > org.codehaus.mojo</groupId > </plugin > </plugins > </build > </profile > </profiles > <dependencies > <dependency > <groupId > com.xxx</groupId > <artifactId > xxx-model</artifactId > <version > 1.0.0-SNAPSHOT</version > </dependency > </dependencies > </project >
附录
本地存在依赖,但是构建时依然提示依赖获取失败,并从远程获取。遇到这种问题可以尝试删除_remote.repositories文件,其作用是用来标示资源的来源,可以帮助Maven确定资源是否来自远程仓库,以及是否需要从远程仓库获取更新
1 find ./repository/ -name "_remote.repositories" | xargs rm -f;
有时我们在离线环境上构建,也会修改maven的mirror地址改为本地文件
1 <url > file:///usr/local/maven/repository</url >
使用docker可以快速搭建一个nexus服务,至于创建仓库上传jar包,可以参考http://119.23.147.120/archives/nexus-si-fu-da-jian-ji-lu 这里就不赘述了
docker-compose.yaml 1 2 3 4 5 6 7 8 9 10 11 12 version: "3" services: nexus: image: sonatype/nexus3:3.19.1 container_name: nexus ports: - "8081:8081" volumes: - ./data:/nexus-data - /etc/localtime:/etc/localtime privileged: true
对于jar包和docker镜像,我们都有仓库进行管理,其实对于tar包也可以上传nexus进行管理,比如我们可以创建一个application-packages仓库,然后:
1 2 mvn deploy:deploy-file -Durl=http://192.168.141.13:8081/repository/application-packages/ -DrepositoryId=xxx \ -DgroupId=com.xxx -DartifactId=xxx-serviceA -Dversion=$app_version -Dpackaging=tar -Dfile=target/$app_tar
另外通过http的put请求也可以直接上传文件,这样就可以搞一个脚本来将外网下载下来的仓库上传到内网的nexus服务了
mavenimport.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 #!/bin/bash while getopts ":r:u:p:" opt; do case $opt in r) REPO_URL="$OPTARG " ;; u) USERNAME="$OPTARG " ;; p) PASSWORD="$OPTARG " ;; esac done find . -type f -not -path './mavenimport\.sh*' -not -path '*/\.*' -not -path '*/\^archetype\-catalog\.xml*' -not -path '*/\^maven\-metadata\-local*\.xml' -not -path '*/\^maven\-metadata\-deployment*\.xml' | sed "s|^\./||" | xargs -I '{}' curl -u "$USERNAME :$PASSWORD " -X PUT -v -T {} ${REPO_URL} /{} ;
在repository目录中执行如下命令便可以上传所有jar
1 sh mavenimport.sh -u admin -p admin -r http://192.168.141.13:8081/repository/release/
如果想将jar包上传Maven中央仓库,可以参考https://segmentfault.com/a/1190000038362019 已经写的非常详细了,可以参考之前上传的一个完整示例:https://github.com/shanhm1991/spring-fom/blob/master/pom.xml
参考: