设置阿里云国内镜像
从Maven的中央仓库下载jar包速度很慢,可以通过在settings.xml
中配置一个国内镜像来提高下载速度,一般用阿里云镜像。注意,如果你同时有多个配置文件,必须要修改当前用户目录下的~/.m2
下的settings.xml
才有效。
1
2
3
4
5
6
7
8
| <mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
|
修改配置后,需要重新打开命令行窗口,或者重启IDE才能生效。通常公司会在内网中自己架设一个Nexus私服,项目成员将镜像节点配置为公司的私服,这样无需连接外网也可以下载Jar包,而且速度更快。阿里云镜像实际上就是一个对外公开的Nexus私服仓库。
指定编译的JDK版本
在settings.xml
中配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<jdk>1.8</jdk>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
|
这是一种全局配置,当Maven项目的pom中没有指定编译插件的jdk版本时,会自动使用全局配置的版本。
offline节点的作用
企业项目通常更新很快,每天都会出一个snapshot包,在当天里第一次启动项目时,如果中央仓库里的snapshot包更新了,则会自动下载到本地。
如果你此时在本地更改了项目代码并打包过一次,在启动项目时恰巧中央仓库的jar包更新了,就会把本地打包好的jar包给替换掉了。此时必须重新打包一次,才能使用你之前修改的项目代码。为了避免这种情况,可以将settings.xml
中的offline设为true。
1
| <offline>true</offline>
|
离线模式下的Maven是不会自动帮你从远处库下载项目依赖,该配置只建议在本地使用,因为如果pom中引入了新的依赖,此时Maven无法自动下载该依赖,会导致项目编译报错,如下:
1
| Caused by: org.eclipse.aether.transfer.ArtifactNotFoundException: Cannot access spring-releases (https://repo.spring.io/libs-release) in offline mode and the artifact org.springframework.boot:spring-boot-loader-tools:jar:2.0.5.RELEASE has not been downloaded from it before.
|
这种时候需要重新将offline节点改为false。
optional标签
当不需要将某个依赖向外传递时,可以通过将optional标签设置为true来实现,比如当前项目使用的Lombok依赖不需要被其他项目所使用到:
1
2
3
4
5
6
| <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<optional>true</optional>
</dependency>
|
编码GBK的不可映射字符
执行mvn clean package
时报错:编码GBK的不可映射字符
,该问题是因为字符集编码不同导致的,需要在pom中设置统一的编码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <build>
<finalName>demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>utf8</encoding>
</configuration>
</plugin>
</plugins>
</build>
|
这里的source和target的1.7指的是编译时使用jdk1.7版本;而encoding指定了utf8编码(我测试了下,发现写utf-8也可以)。
打war包的方式
首先要了解Maven的流程:
mvn clean
:清除target
目录mvn compile
:编译文件mvn package
:打包项目mvn install
:把打包后的项目安装到本地仓库,包含了compile和packagemvn deploy
:把打包后的项目上传到远程仓库(比如自行搭建的私服),包含了install
关于打war包,有多种方式,如下:
- 使用Maven本身的打包方式,把
mvn packaging
设为war
,即可使用package命令打成war包。 - 使用maven的war命令:
mvn war:exploded
或者mvn war:war
,后一个命令会打包出一个war包以及war包解压后的目录文件,前一个命令只会打包出war包解压后的目录文件。
* 使用tomcat7
插件命令打包:mvn tomcat7:exec-war
或者mvn tomcat7:exec-war-only
,如下:
1
2
3
4
5
6
7
8
9
| <build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</build>
|
tomcat7
插件支持以下命令:
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
| // 查看帮助信息
tomcat7:help
// 把WAR部署到Tomcat
tomcat7:deploy
// 把WAR部署到Tomcat,但是不管理包的生命周期
tomcat7:deploy-only
// 重新部署WAR到Tomcat
tomcat7:redeploy
// 重新部署WAR到Tomcat,但是不管理包的生命周期
tomcat7:redeploy-only
// 从Tomcat中卸载WAR
tomcat7:undeploy
// 使用内嵌的Tomcat服务把当前应用作为动态web应用运行
tomcat7:run
// 使用内嵌的Tomcat服务运行当前应用的打包文件war
tomcat7:run-war
// 使用内嵌的Tomcat服务运行当前应用的打包文件war,但是不管理包的生命周期
tomcat7:run-war-only
// 创建一个可执行的jar文件,其中包含了Apache Tomcat,只要执行java -jar xxx.jar即可运行web应用
tomcat7:exec-war
// 创建一个可执行的jar文件,但是不管理包的生命周期
tomcat7:exec-war-only
// 关闭所有的可执行的服务
tomcat7:shutdown
// 创建一个可执行的war文件
tomcat7:standalone-war
// 创建一个可执行的war文件
tomcat7:standalone-war-only
|
最后两个命令在mvn tomcat7:help
中的解释是一模一样的,暂时不知道和exec-war
有何区别。
mvn clean package
的执行顺序
- 使用清理插件
maven-clean-plugin
清理已有的target目录(使用了clean才有这一步) - 使用资源插件
maven-resources-plugin
处理资源文件 - 使用编译插件
maven-compiler-plugin
编译所有源文件生成class文件到target/classes目录下 - 使用资源插件
maven-resources-plugin
处理测试用的资源文件 - 使用编译插件
maven-compiler-plugin
编译测试用的源码正常class文件到target/test-classes目录下 - 使用测试插件
maven-surefire-plugin
运行测试用例 - 使用打包插件
maven-jar-plugin
对编译后生成的文件进行打包,包名和配置的finalName一致,打包后的文件存放在target目录下
不管是compile、package还是install等,前三个步骤都是必不可少的。
设置下载源码和文档
方法很多,这里说下3种。
使用Maven命令
在项目根目录下执行:
1
2
| mvn dependency:sources
mvn dependency:resolve -Dclassifier=javadoc
|
修改配置文件进行全局配置
打开当前用户目录下的全局配置文件/.m2/settings.xml
,添加如下配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
| <profiles>
<profile>
<id>downloadSources</id>
<properties>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</properties>
</profile>
</profiles>
<activeProfiles>
<activeProfile>downloadSources</activeProfile>
</activeProfiles>
|
在IDE工具中配置
可以在IDE工具,诸如Eclipse、idea等中修改Maven插件的一些配置,在Eclipse中配置如下:
Window
-> Preferences
-> Maven
-> 勾选Download Artifact Sources
和Download Artifact JavaDoc
Maven聚合工程怎么变回普通的Maven工程
Maven聚合工程的父工程的packaging是pom,如果我们将其改为jar,会立刻报错:
1
| Project build error: 'packaging' with value 'jar' is invalid. Aggregator projects require 'pom' as packaging.
|
对于聚合工程来说,所有的子工程会被放置到父工程的目录下,然后在父工程的pom文件里会有如下的节点:
1
2
3
| <modules>
<module>test-child</module>
</modules>
|
这些modules节点正是引用了父工程pom文件的子工程。
解决方法
将父工程的modules节点全部去掉,注释掉也行,再将packaging的值从pom改成jar或者war,接着保存,修改成功。
虽然修改成功了,但是去父工程的目录下 ,你会发现那些子工程依然存在着。不过这些工程已经很父工程没有关系了,因为父工程已经不再是聚合工程了,可以将这些子工程移除掉。
java.lang.StackOverflowError
服务器上的Jenkins在集成项目时报错如下:
1
2
3
4
| error compiling: java.lang.StackOverflowError -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
|
错误很明显,堆栈溢出,要么是jvm设置的线程栈太小,要么是代码有问题。而服务器每天晚上都会自动集成,jvm参数也不会有人去改过,很明显是最近提交的代码有问题。
审查了代码,发现是某个测试类中有段代码里调用了一个API,该API又调用了四百多个API。这个API的目的是检测pojo里的字段是否和数据库的字段匹配,一个字段对应一个API,总共有四百多字段。Jenkins在跑单元测试跑到这里就堆栈溢出了。
毕竟是测试类的代码,改动时逻辑照旧,只不过不继续在一个API去直接调用四百个API,而是将其分成两个新的方法,每个方法各自调用两百个API,然后原本的API调用新增的两个方法。
之后提交代码,重新让Jenkins集成代码,发现不再报错。
当然了,也可以直接在启动脚本里简单粗暴调大线程栈大小:
1
2
3
| set MAVEN_OPTS=-Xss4096k
或者
set MAVEN_OPTS=-Xss2m
|
这里顺便贴一下项目脚本原本设置的参数:
1
2
3
4
5
| echo off
setlocal
set MAVEN_DEBUG_OPTS=-Duser.timezone=GMT+8 -Xdebug -Xmx4096M -XX:PermSize=128M -XX:MaxPermSize=512M -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8088,server=y,suspend=n
mvn spring-boot:run
endlocal
|
无法下载2.1.7.js7版本的itext依赖
编译项目时报错如下:
1
2
3
| Failed to collect dependencies at net.sf.jasperreports:jasperreports:jar:6.10.0
-> com.lowagie:itext:jar:2.1.7.js7: Failed to read artifact descriptor for com.lowagie:itext:jar:2.1.7.js7:
Could not transfer artifact com.lowagie:itext:pom:2.1.7.js7
|
一开始以为是网络不好连接不上远程库,或者远程库没有该jar包,后来发现在Maven中央仓库里也没找到2.1.7.js7
版本的itext
依赖。在Stack Overflow上查询后发现有不少人遇到同样的问题,都是由于使用了某个版本的jasperreports
,最终导致了该错误。
由于在jasperreports的pom文件里指定了2.1.7.js7
版本的itext
依赖,而目前的Maven中央仓库或其他镜像仓库里是不存在这种带有js7
等后缀版本。该版本是jasperreports为了修复一些bug而打上了补丁的版本,但是并没有release到中央库里,不过这些bug在更高版本里也被修复了,可以使用更高版本的itext来避免这些bug。
解决方法
排除jasperreports中的itext依赖并自行指定版本,pom如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.10.0</version>
<exclusions>
<exclusion>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>
|
这里的itext版本根据自身实际情况指定,目前itext已停止维护,并从4.2.2之后的版本开始从com.lowagie.itext
迁移到com.itextpdf.itextpdf
,有需要的话可以使用更高版本的itextpdf依赖,pom如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <dependency>
<groupId>jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.10.0</version> <!--(or higher)-->
<exclusions>
<exclusion>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version> <!--(or higher)-->
</dependency>
|
Java加载外部字体时报错FontFormatException
,Maven在编译项目时使用-X
开启debug模式同样可以看到该异常。原因是使用了resource
插件的filtering
功能,filtering
为true
时可以使用系统变量或者启动参数来替换资源文件的占位符${}
的值。但也会导致Maven打包时对资源文件重新进行编译,而这些资源文件自身有着特定的编码,被Maven重新编码后文件损坏,于是就会触发该异常。
处理方式很简单,不再过滤特定的资源文件,如字体、excel等,避免对其重新编码。下面是demo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<!-- 过滤后缀不需要转码的文件后缀名.crt/.ttf-->
<nonFilteredFileExtensions>
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
<nonFilteredFileExtension>xlsx</nonFilteredFileExtension>
<nonFilteredFileExtension>xls</nonFilteredFileExtension>
<nonFilteredFileExtension>zip</nonFilteredFileExtension>
<nonFilteredFileExtension>cer</nonFilteredFileExtension>
<nonFilteredFileExtension>pfx</nonFilteredFileExtension>
<nonFilteredFileExtension>py</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
|
这个nonFilteredFileExtensions
标签和exclude
是不同的,前者依然会打包文件,只是不对其进行重新编译;后者是在打包时直接排除对应的文件。
include
是指定打包时需要哪些文件,但include
和exclude
的配置冲突时,以后者为准。
引入本地jar包
两种方式,一种是把jar包安装到本地仓库后再引入;一种是直接用systemPath
直接引入一个外部的jar包。如果只是本地开发,可以用前一种方式;如果是协同开发,个人更倾向于后一种方式。
方式一:安装到本地仓库
下面是将jar包安装到本地的命令:
1
| mvn install:install-file -Dfile=D:\lib\test.jar -DgroupId=com.test -DartifactId=test -Dversion=0.0.1 -Dpackaging=jar
|
-Dfile
指明需要被安装的jar包路径。
-DgroupId
和-DartifactId
指定jar包的唯一依赖路径,注意别跟其他的本地依赖路径冲突就行。
-Dversion
指定依赖的版本。
-Dpackaging
指明打包方式。
安装到本地仓库后可以像普通依赖那样引入:
1
2
3
4
5
| <dependency>
<groupId>com.test</groupId>
<artifactId>test</artifactId>
<version>0.0.1</version>
</dependency>
|
方式二:systemPath
直接引入
1
2
3
4
5
6
| <dependency>
<groupId>com.test</groupId>
<artifactId>test</artifactId>
<scope>system</scope>
<systemPath>${basedir}/src/main/resources/lib/test.jar</systemPath>
</dependency>
|
${basedir}
是Maven的内置属性,代表项目根目录。
压缩js+css代码
借助yuicompressor
插件来完成:
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
| <!-- Maven全局属性,统一编码 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 构建相关配置 -->
<build>
<!-- maven插件配置 -->
<plugins>
<plugin>
<!-- YUI Compressor Maven压缩插件 -->
<groupId>net.alchim31.maven</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<version>1.3.0</version>
<configuration>
<!-- 读取js,css文件采用UTF-8编码 -->
<encoding>UTF-8</encoding>
<!-- 不显示js可能的错误 -->
<jswarn>false</jswarn>
<!-- 若存在已压缩的文件,会先对比源文件是否有改动。有改动便压缩,无改动就不压缩 -->
<force>false</force>
<!-- 在指定的列号后插入新行 -->
<linebreakpos>-1</linebreakpos>
<!-- 压缩之前先执行聚合文件操作 -->
<preProcessAggregates>true</preProcessAggregates>
<!-- 压缩后保存文件后缀 -->
<suffix>.min</suffix>
<!-- 源目录,即需压缩的根目录 -->
<sourceDirectory>${basedir}/mobile</sourceDirectory>
<!-- 压缩js和css文件 -->
<includes>
<include>**/*.js</include>
<include>**/*.css</include>
</includes>
<!-- 以下目录和文件不会被压缩 -->
<excludes>
<exclude>**/*.min.js</exclude>
<exclude>**/*.min.css</exclude>
<exclude>scripts/data/*.js</exclude>
</excludes>
<!-- 压缩后输出文件目录 -->
<outputDirectory>${basedir}/mobile</outputDirectory>
<!-- 聚合文件 -->
<aggregations>
<aggregation>
<!-- 合并每一个文件后插入一新行 -->
<insertNewLine>true</insertNewLine>
<!-- 需合并文件的根文件夹 -->
<inputDir>${basedir}/mobile/scripts</inputDir>
<!-- 最终合并的输出文件 -->
<output>${basedir}/mobile/scripts/app/app.js</output>
<!-- 把以下js文件合并成一个js文件,是按顺序合并的 -->
<includes>
<include>app/core.js</include>
<include>app/mlmanager.js</include>
<include>app/tmpl.js</include>
<include>app/ui.js</include>
</includes>
</aggregation>
</aggregations>
</configuration>
</plugin>
</plugins>
</build>
|
跳过测试案例
有两种方式,一个是启动时添加参数:-DskipTests
;一种是添加参数-Dmaven.test.skip=true
。
前者不执行测试用例,但是会编译测试类到target/test-classes
目录。
后者既不执行测试用例,也不编译测试类。
参考链接
警告
本文最后更新于 September 7, 2021,文中内容可能已过时,请谨慎使用。