代码之旅

I love Coding !

我的Effective Maven使用笔记

常用 mvn 命令

mvn输出增加线程ID

1
MAVEN_OPTS=-Dorg.slf4j.simpleLogger.showThreadName=true mvn <goals>

https://maven.apache.org/maven-logging.html

pom.xml 设置

定义源码位置

设置Maven 源代码的位置

1
2
3
<properties>
<src.dir>src/main/java</src.dir>
</properties>

jar包名

maven的包名可以通过finalName配置。

1
2
3
<build>
<finalName> ${project.artifactId}-${project.version}</finalName>
</build>

时间戳

如果想在包名上增加UTC时间戳,可以通过在finalName中添加属性maven.build.timestamp.

1
2
3
<build>
<finalName> ${project.artifactId}-${maven.build.timestamp}</finalName>
</build>

maven.build.timestamp的格式是通过maven.build.timestamp.format参数设置的。

1
2
3
<properties>
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
</properties>

maven自带时间组件时区是只能使用UTC,要使用正确的时间,需要另一个插件build-helper-maven-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
<build>
<!-- 要将maven.build.timestamp替换成build.time -->
<finalName>${project.artifactId}-${build.time}</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>timestamp-property</id>
<goals>
<goal>timestamp-property</goal>
</goals>
<configuration>
<name>build.time</name>
<pattern>yyyyMMddHHmm</pattern>
<locale>zh_CN</locale>
<timeZone>GMT+8</timeZone>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Build Helper

build-helper-maven-plugin插件提供了 Maven 使用中的多个常用小功能。

  • 管理源码目录
    • add-source:添加源码目录
    • add-test-source:添加测试源码目录
  • 管理资源目录
    • add-resource:添加资源目录
    • add-test-resource:添加测试资源目录
  • 版本管理
    • parse-version:解析项目版本为属性
    • released-version:解析项目最新发布版本
  • attach-artifact: 附件管理
  • timestamp-property:设置时间戳属性
  • rootlocation: 多模块构建的根文件夹
  • … …

依赖问题

当项目开发维护了一段时间时,经常会有项目打包速度慢,jar依赖多,依赖关系错综复杂,这种问题是项目维护最常见的问题,由于开发人员在bugfix或者feature开发时往往只是往项目中添加jar依赖,我们需要分析出项目中哪些依赖是用到的,哪些依赖是多余的。

为了解决这一问题,可以通过 mvn dependency:analyze 来查看依赖问题(analyze命令用于单独执行,也可以在pom配置中加上analyze-onlygoal和mvn打包一起使用)。

通常会有如下的输出日志:

1
2
3
4
5
6
7
[INFO] --- maven-dependency-plugin:2.8:analyze (default-cli) @ xxxproject ---
[WARNING] Used undeclared dependencies found:
[WARNING] org.springframework:spring-beans:jar:4.0.0.RELEASE:compile
[WARNING] org.springframework:spring-context:jar:4.0.0.RELEASE:compile
[WARNING] Unused declared dependencies found:
[WARNING] com.alibaba:dubbo:jar:2.5.3:compile
[WARNING] com.baidu.disconf:disconf-client:jar:2.6.32:compile

对于日志中的Used undeclared dependencies foundUnused declared dependencies found我们分别介绍下处理方案。

Used undeclared dependencies found

Used undeclared dependencies found 是指某些依赖的包在代码中有用到它的代码,但是它并不是直接的依赖(就是说没有在pom中直接声明),是通过引入传递的包。

因此,对于Used undeclared dependencies found的包,可以通过直接在pom文件的依赖中显式声明。

Unused declared dependencies found

Unused declared dependencies found 指我们在pom中声明了依赖,但是在实际代码中并没有用到这个包!也就是多余的包。 这个时候我们就可以把这个依赖从pom中剔除。

注意: 这里说的代码没有用到,指的是在代码中没有显式引用,并不是意味着真的没有用到这些包(例如spi方式引入的代码).对于这种情况可以在 <configuration> 中的<ignoredUsedUndeclaredDependencies> 配置中增加这些包。

格式化

maven-surefire-plugin

maven-surefire-plugin的Junit格式化配置

  • 在TestCase中增加@DisplayName注解,用于展示自定义测试名。
  • 树状结构打印单元测试结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- 注意版本要高于3.0.0-M4 -->
<version>3.1.2</version>
<dependencies>
<dependency>
<groupId>me.fabriciorby</groupId>
<artifactId>maven-surefire-junit5-tree-reporter</artifactId>
<version>0.1.0</version>
</dependency>
</dependencies>
<configuration>
<reportFormat>plain</reportFormat>
<consoleOutputReporter>
<disable>true</disable>
</consoleOutputReporter>
<statelessTestsetInfoReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter"/>
</configuration>
</plugin>

日志输出样例:

1
2
3
4
5
6
7
8
9
[INFO] |
[INFO] +-- 语法解析测试
[INFO] | +-- [OK] parse01 - 0.001 ss
[INFO] | +-- [OK] parse02 - 0.001 ss
[INFO] | +-- [OK] parse03 - 0.010 ss
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 70, Failures: 0, Errors: 0, Skipped: 0

Sortpom Maven Plugin

Sortpom插件帮助排序maven pom 文件。

重复资源

duplicate-finder-maven-plugin

duplicate-finder-maven-plugin 用于查找classpath下重复的class和resource。

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
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<version>1.3.0</version>
<executions>
<execution>
<id>default</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<configuration>
<!-- 用于设置忽略的resource-->
<ignoredResourcePatterns>
<ignoredResourcePattern>parquet.thrift</ignoredResourcePattern>
<ignoredResourcePattern>about.html</ignoredResourcePattern>
</ignoredResourcePatterns>
<!-- 用于设置忽略的class-->
<ignoredClassPatterns>
<ignoredClassPattern>shaded.parquet.it.unimi.dsi.fastutil.*</ignoredClassPattern>
<ignoredClassPattern>module-info</ignoredClassPattern>
</ignoredClassPatterns>
</configuration>
</configuration>
</plugin>

如果在日志中发现重复的类,可以将通过mvn dependency:tree找到对应的依赖将其exclude掉。

规则检查

Maven Enforcer

Maven Enforcer插件是一个用于强制执行特定规则的Maven插件。它可以帮助您确保项目中的依赖项和构建环境符合特定的要求,例如强制使用特定版本的Java、强制执行依赖项收敛规则等。Maven Enforcer插件非常有用,可以确保项目的稳定性和可靠性。

Maven Enforcer插件提供了许多规则,可以根据需要进行配置。例如:

  • Require Java Version:强制要求使用特定版本的Java。
  • Require Maven Version:强制要求使用特定版本的Maven。
  • Require Plugin Versions:强制要求所有插件定义版本。
  • Dependency Convergence:强制要求依赖项收敛,即确保项目中只使用一个版本的依赖项。
  • Banned Dependencies:禁止使用特定的依赖项。
  • Require Property:强制要求使用特定的属性。
  • Require Upper Bound Dependencies: 要求所有依赖的符合最低版本。

参考

拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:

  1. 每个顶点出现且只出现一次。
  2. 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
graph TD
id1((1)) --> id2((2))
id1((1)) --> id4((4))
id2((2)) --> id4((4))
id4((4)) --> id3((3))
id2((2)) --> id3((3))
id4((4)) --> id5((5))
id3((3)) --> id5((5))

有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。

阅读全文 »

shell 脚本选项参数解析通常有 3 种方法。

  • 解析参数数组
  • 基于getopts
  • 基于getopt
阅读全文 »

概念介绍

布尔基础:

  • 逻辑表达式: 由逻辑变量和与 \land ,或 \lor ,非 ¬\neg 3种运算符连接所构成的表达式。

  • 析取式: 表达式之间都通过逻辑或连接的复合表达式。

  • 合取式: 表达式之间都通过逻辑与连接的复合表达式。

  • 合取范式 (Conjunctive Normal Form)2 是命题公式的一个标准型,它由一系列析取子句 用合取操作连接而来。如 (a)(a¬c)(bc)(a) \land (a \lor \neg c) \land (b \lor c)

  • 与之相反,析取范式 (Disjunctive Normal Form) 是命题公式的另一个标准型,它由一系列 合取子句 用 析取操作 连接而来。如 (a)(a¬c)(bc)(a) \lor (a \land \neg c) \lor (b \land c)

表达式化简:

  • 表达式相等: 两个表达式具有同样的变量,且对于变量的任意一组取值,表达式的值均相等,这两个表达式是相等的
  • 最小项: 如果某个表达式的某个乘积(与)项包含了表达式的全部变量,其中每个表达式都以原变量或是反变量的形式出现。n个变量可以有2^n个最小项。
  • 主析取式: 可以将表达式化简为全部由最小项组成的唯一表达式,也被称为主析取式(符合析取范式).
阅读全文 »

JMH 是openJDK项目下的JVM工具,用于构建,运行和分析用Java和其他语言编写的针对JVM的nano/micro/milli/macro微基准测试。

阅读全文 »

随着多核芯片的广泛使用,线程是提升性能的首选方案。适当提升一点线程数会很好;事实上,拥有太多线程可能会使程序陷入困境。过多的线程主要对两个方面有影响:

  • 首先,在太多线程之间划分固定数量的工作会使每个线程分到的工作太少,以至于启动和终止线程的开销会影响有用的工作。
  • 其次,运行太多线程会因为共享有限硬件资源,从而导致产生过多的开销。
阅读全文 »

split命令可以将一个大文件分割成很多个小文件,有时需要将文件分割成更小的片段,比如为提高可读性,生成日志等。

阅读全文 »

本文是学习flink源码前的准备工作。在开始之前,先做好准备工作:

  • 安装JDK8+,Flink 依赖 Java 8 或更新的版本来进行构建。
    • 自flink1.15 开始需要Java 11(如无必要不使用更高版本的JDk去build)
  • 安装Maven 3 ( Maven 3.3.x 可以构建 Flink,但是不能正确地屏蔽掉指定的依赖。Maven 3.2.5 可以正确地构建库文件)
  • 安装Scala 2.11或2.12
    • 自flink1.15 开始需要Scala 2.12
阅读全文 »

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  • 主版本号:当你做了不兼容的 API 修改,
  • 次版本号:当你做了向下兼容的功能性新增,
  • 修订号:当你做了向下兼容的问题修正。

先行版本号及版本编译元数据可以加到主版本号.次版本号.修订号 的后面,作为延伸。

阅读全文 »

生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。

阅读全文 »