免费下载!由 O’Reilly 出版的《NGINX 完全指南》中文版已正式上线 了解详情
写点什么

Maven 实战(二)——POM 重构之增还是删

  • 2010-12-27
  • 本文字数:3124 字

    阅读完需:约 10 分钟

重构是广大开发者再熟悉不过的技术,在 Martin Fowler 的《重构——改善既有代码的设计》一书中,其定义为“重构 (名词): 对软件内部结构的一种调整, 目的是在不改变软件之可察行为前提下, 提高其可理解性, 降低其修改成本.”以及“重构 (动词): 使用一系列重构准则 (手法), 在不改变软件之可察行为前提下, 调整其结构.”。重构能够改善软件设计,使代码更易读,更容易找出 bug,并帮助你更快速地编码。较之于一般的代码来说,Maven 的 POM 简单很多,不过随着项目的成长,模块的增多,POM 的内容也会变多,这个时候,我们可以对 POM 进行重构,在保持构建成功的前提下,简化 POM 内容,使其更简洁易懂。

前提

大家都知道,如果没有单元测试为前提,对代码进行重构是非常危险的。同样,在重构 POM 之前,项目应该有足够的自动化测试保证 POM 重构不会破坏构建。例如,重构 POM 的时候可能会删除或添加依赖,造成依赖版本变化,依赖范围变化,插件版本变化等等,这些变化可能会导致项目编译失败,或者测试失败。在自动化测试及构建的基础上,最好能够有持续集成环境,以保证 POM 的修改可能造成的问题能够及时的被发现和修复。笔者目前工作的项目有一个对应的持续集成任务,该任务基于 Hudson,每 10 分钟检查一次 SCM,如果发现变更则构建项目,并反馈结果。这样,我就不用担心自己修改 POM 会引入潜在的问题。

增还是删

有时候这个答案是很显然的,当你的 POM 中存在一些依赖或者插件配置,但实际代码没有用到这些配置的时候,应该尽早删掉它们以免给人带来困惑。

还有一种常见的情况,我们可以删掉一些 POM 的元素,例如 POM 中配置了继承,当前模块与父模块使用同样的 groupId 和 version 时,就可以将元素删除,因为它们可以从父模块继承而来,重复配置没有什么意义。

复制代码
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.juvenxu.sample</groupId>
<artifactId>sample-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>sample-foo</artifactId>
<packaging>jar</packaging>
...
</project>

上述配置就 sample-foo 就没有 groupId 和 version,需要注意的是,artifactId 是不能被删除的,因为该元素不能也不应该被继承,父子模块应当使用不同的 artifactId 值。

除了删之外,有些时候我们还需要在 POM 中增加一些 XML 元素,目的是为了让 POM 更清晰易读,且保证 Maven 构建的稳定性。考虑如下的插件配置:

复制代码
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>

虽然没有 groupId 及 version,但这段配置是完全合法的。当插件没有 groupId 配置的时候,Maven 会认为它是官方插件而自动使用 org.apache.maven.plugins 作为 groupId,当插件没有 version 配置的时候,Maven 则会使用最新的版本(Maven 2 会使用最新的版本,包括 SNAPSHOT,而 Maven 3 则只使用最新的非 SNAPSHOT 版本)。这段配置有两个问题,首先,如果让一个不熟悉 Maven 的开发者来看这段配置,他会感到费解,groupId 和 version 究竟是什么呢?这与不清晰的代码是一个意思,有时候一些程序员为了让代码更短,会采用一些奇怪的语法和变量名,虽然代码量是少了,但沟通成本增加了,得不偿失。其次,让 Maven 猜测版本会有潜在的风险,因为插件的最新版本可能会变化,而这种变化对于 Maven 使用者来说通常是隐藏的,特别是在 Maven 2 中,甚至可能引入 SNAPSHOT 版本的插件,这是非常危险的。基于这两个原因,使用插件的时候,我们应当配置清楚 groupId 和 version,如:

复制代码
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>

基于类似的原因,在配置项目依赖的时候,我们也应当一直显式地写明依赖版本,以避免 Maven 在不同的时刻引入不同版本的依赖而导致项目构建的不稳定。

除了上面提到的增删点之外,Maven 官方还提供了一个非常有用的 Maven Dependency Plugin 来帮助我们分析项目中哪些依赖配置应该删除,那些依赖配置应该增加。Maven Dependency Plugin 的 analyze 目标能够帮助分析项目依赖,例如运行命令 mvn dependency:analyze ,可以看到如下输出:

复制代码
[INFO] --- maven-dependency-plugin:2.1:analyze (default-cli) @ sample-bar ---
[WARNING] Used undeclared dependencies found:
[WARNING] org.springframework:spring-context:jar:2.5.6:compile
[WARNING] Unused declared dependencies found:
[WARNING] org.springframework:spring-core:jar:2.5.6:compile
[WARNING] org.springframework:spring-beans:jar:2.5.6:compile
[INFO] ------------------------------------------------------------------------

这里的 Used undeclared dependencies 是指那些在项目中直接使用到的,但没有在 POM 中配置的依赖。例如该例中可能项目中的一些类有关于 spring-context 的 Java import 声明,但 spring-context 这个依赖实际是通过传递性依赖进入 classpath 的,这就意味者潜在的风险。一般来说我们对直接依赖的版本变化会比较清楚,因为那是我们自己直接配置的,但对于传递性依赖的版本变化,就会比较模糊,当这种变化造成构建失败的时候,就很难找到原因。因此我们应当增加这些 Used undeclared dependencies 。

依赖分析还提供了 Unused declared dependencies 供我们参考,这表示那些我们配置了,但并未直接使用的依赖。需要注意的时,对于这些依赖,我们不该直接简单地删除。由于 dependency:analyze 只分析编译主代码和测试代码使用的依赖,一些执行测试和运行时的依赖它发现不了,因此还需要人工分析。通常情况,Unused declared dependencies 还是能帮助我们发现一些无用的依赖配置。

最后,还一些重要的 POM 内容通常被大多数项目所忽略,这些内容不会影响项目的构建,但能方便信息的沟通,它们包括项目 URL,开发者信息,SCM 信息,持续集成服务器信息等等,这些信息对于开源项目来说尤其重要。对于那些想了解项目的人来说,这些信息能他们帮助找到想要的信息,基于这些信息生成的 Maven 站点也更有价值。相关的 POM 配置很简单,如:

复制代码
<project>
<description>...</description>
<url>...</url>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement>
<mailingLists>...</mailingLists>
<scm>...</scm>
</project>

小结

无论是对 POM 内容进行增还是删,其目的都是一样的,就是 _ 为了让 POM 更清晰易懂且让构建更稳定 _。从这点来说,POM 重构与一般的代码重构是类似的。需要谨记的是,重构的前提是完善的自动化测试和持续集成。本文介绍的单个 POM 规模的重构,下篇文章笔者会介绍多模块项目的 POM 重构等内容。

关于作者

许晓斌(Juven Xu),国内社区公认的 Maven 技术专家、Maven 中文用户组创始人、Maven 技术的先驱和积极推动者。对 Maven 有深刻的认识,实战经验丰富,不仅撰写了大量关于 Maven 的技术文章,而且还翻译了开源书籍《Maven 权威指南》,对 Maven 技术在国内的普及和发展做出了很大的贡献。就职于 Maven 之父的公司,负责维护 Maven 中央仓库,是 Maven 仓库管理器 Nexus(著名开源软件)的核心开发者之一,曾多次受邀到淘宝等大型企业开展 Maven 方面的培训。此外,他还是开源技术的积极倡导者和推动者,擅长 Java 开发和敏捷开发实践。他的个人网站是: http://www.juvenxu.com

2010-12-27 00:1820036

评论 1 条评论

发布
用户头像
新get到的点:
1. 重复的东西应该删除,尤其是在多模块项目中,共用依赖可以提取到parent pom dependencyManagement中,可以有效避免版本不一致问题。
2. 保证依赖的明确性。groupId,version等
2021-12-15 21:42
回复
没有更多了
发现更多内容

【Java】代码重构时,为什么禁止在方法内对对象类型的入参赋值

恒生LIGHT云社区

Java 代码规范 java代码规范

恒源云(GPUSHARE)_opencv(论文笔记)

恒源云

深度学习

使用docker安装elk环境

小鲍侃java

签约计划第二季

使用自定义注解打印logstash日志

小鲍侃java

签约计划第二季

编曲时如何在FL Studio卷帘窗口修改单个音符音量

懒得勤快

架构实战营 模块七作业

felix

「架构实战营」

青藤解密:72%客户容器规模>100个,[镜像安全]谁来保护?

青藤云安全

镜像安全

使用linux搭建单机elk环境

小鲍侃java

签约计划第二季

logback获取bootstrap.yml配置

小鲍侃java

签约计划第二季

VirtualBox 网络配置 NAT + Host-Only

李鑫磊

Kubernetes 虚拟机 网络配置 virtualbox

莫要寻找可能不存在的答案

FunTester

学习 解决方案 自学 FunTester 思路

前端避坑指南丨辛辛苦苦开发的APP竟然被判定为简单网页打包?

YonBuilder低代码开发平台

大前端 APP开发 APICloud 跨端开发

使用logstash获取springboot控制台日志并传输到elasticsearch

小鲍侃java

签约计划第二季

elk客户端与springboot整合

小鲍侃java

签约计划第二季

mPaaS 月度小报|魔方卡片(Cube)公测,十个卡片模板任意使用

蚂蚁集团移动开发平台 mPaaS

小程序 消息推送 移动开发 API网关 cube

模仿UP主,用Python实现一个弹幕控制的直播间!

Zhendong

Python

做一款互联网内容平台,到底要懂多少AI?

百度开发者中心

AI

【AI最前线】精准优质-资讯|分享|热议第42期

百度大脑

数据中台的OneID是个什么鬼

用友BIP

数据中台 主数据

高可用 | repmgr 构建 PostgreSQL 高可用集群部署文档【建议收藏】

RadonDB

数据库 postgresql RadonDB

安全RCE之未授权访问分析

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 漏洞挖掘

在 ABAP 里模拟实现 Java Spring 的依赖注入

Jerry Wang

Java 28天写作 SAP abap 12月日更

恒拓高科WorkPlus - 政企IM私有化解决方案

WorkPlus

使用logstash监控微服务项目,并制定索引格式

小鲍侃java

签约计划第二季

在有道 | 同宇:一个正在老去的程序员

有道技术团队

程序员 技术人 工程师思维 人物访谈

数仓和数据中台长期霸权,数据湖最稳

用友BIP

数据胡

面试腾讯:渗透测试工程师题型汇总

喀拉峻

面试 网络安全 安全 渗透测试

在 Windows 笔记本上调试运行在 iOS 设备上的前端应用

Jerry Wang

大前端 web api iOS Developer 28天写作 12月日更

荣耀周榜公布!你上榜了么?2022.1.17-1.23

InfoQ写作社区官方

话题讨论 热门活动

JVM中的对象及引用

Ayue、

技术专题合集

Maven实战(二)——POM重构之增还是删_Java_许晓斌_InfoQ精选文章