跳至正文
来两杯美式
返回

Maven 入门到精通:依赖管理、生命周期与多模块项目

By 来两杯美式
发布于更新于

这是一篇发布于 2017 年的早期 Maven 教程。内容按当年的视角写就:Java 8 主流、Spring 4.x 末班车、m2eclipse 是 Eclipse 上的事实标准、kaptcha 还是常用的验证码 jar。命令、截图描述、路径写法都保留当年的风格——这正是把历史笔记迁移到新博客的价值。

但原文写得太短——只覆盖了「仓库」和「生命周期」两小块。下面是我把这些「原文」嵌入完整教程后的全量版,POM 详解、依赖机制、插件、多模块、Profile、版本管理、命令大全、镜像加速、错误排查一并补齐,按当年发布的标准来说足够受用很多年。

另注:文中提到的 jcenter 公共仓库已于 2021 年 2 月关闭,新项目请使用 Maven Central。


一、Maven 解决了什么问题

在 Maven 出现之前,Java 项目的依赖管理是一场噩梦:

Maven 想要做的只有一件事:把以上所有问题标准化

它通过一个叫 pom.xml(Project Object Model)的 XML 文件,声明一个项目「是什么、依赖什么、怎么构建、怎么打包」。Maven 解析这份声明,自动完成下载、编译、测试、打包、部署。

两个核心理念值得一开始就刻进脑子里:


二、Maven 仓库体系

本节为原文整理(出自早期笔记,稍作补全)。

2.1 三类仓库

Maven 仓库分三类:

  1. 本地仓库(Local Repository):本机缓存,第一次执行 Maven 命令时自动创建,默认路径是 ~/.m2/repository
  2. 远程仓库(Remote Repository):又分三种:
    • 中央仓库(Central):Maven 官方维护,地址 https://repo.maven.apache.org/maven2/,所有 pom 文件默认继承超级 POM(super POM),中央仓库地址就在超级 POM 里。
    • 私服:局域网内搭建的特殊远程仓库(如 Nexus、Artifactory),Maven 构建时先从私服下载,私服找不到再访问外网,下载完成还会缓存到私服——团队共用一份缓存。
    • 其它公共库:如 jcenter(2016 年仍流行,2022 年已下线)、Spring 自己的仓库、JBoss 仓库等。

2.2 添加远程仓库

远程仓库可在两处配置:

settings.xml 配法:

<settings>
  <profiles>
    <profile>
      <id>dev</id>
      <repositories>
        <repository>
          <id>aliyun-public</id>
          <url>https://maven.aliyun.com/repository/public</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>false</enabled></snapshots>
        </repository>
      </repositories>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>dev</activeProfile>  <!-- 默认激活的 profile id -->
  </activeProfiles>
</settings>

2.3 镜像(Mirror)

镜像用 <mirrors> 配置,<mirrorOf> 指定覆盖的远程仓库 id——配 * 覆盖所有:

<mirrors>
  <mirror>
    <id>aliyun-mirror</id>
    <mirrorOf>*</mirrorOf>
    <name>Aliyun Maven Mirror</name>
    <url>https://maven.aliyun.com/repository/public</url>
  </mirror>
</mirrors>

大陆开发强烈建议配镜像:当年 repo.maven.apache.org 在大陆访问就慢得离谱,到今天也是。

2.4 仓库优先级与解析顺序

这是原文没讲、但极其重要的一点。Maven 解析一个依赖的顺序是:

  1. 本地仓库(命中直接返回)
  2. 本地 profile / settings.xml 中配置的远程仓库
  3. pom 中声明的远程仓库
  4. 中央仓库(兜底)
  5. 镜像会重定向某些仓库的请求(如把 central 重定向到阿里云)

如果配置的多个远程仓库都包含同一个 jar,Maven 取配置在前面的那个(声明优先)。理解这一点能解释很多「明明中央仓库有,为什么下不下来」的怪事。

2.5 分发到远程仓库(distributionManagement)

要在团队内共享你自己打的 jar,可以在 pom.xml 中配置:

<distributionManagement>
  <repository>
    <id>nexus-releases</id>
    <url>http://nexus.example.com/repository/maven-releases/</url>
  </repository>
  <snapshotRepository>
    <id>nexus-snapshots</id>
    <url>http://nexus.example.com/repository/maven-snapshots/</url>
  </snapshotRepository>
</distributionManagement>

账号密码一般配在 settings.xml(pom.xml 会被提交到代码库,敏感信息绝不能进 pom):

<settings>
  <servers>
    <server>
      <id>nexus-releases</id>
      <username>deployer</username>
      <password>some_password</password>
    </server>
    <server>
      <id>nexus-snapshots</id>
      <username>deployer</username>
      <password>some_password</password>
    </server>
  </servers>
</settings>

然后执行 mvn deploy,Maven 就会把当前构件(artifact)发布到对应仓库。releases 仓库只接受非 SNAPSHOT 版本,snapshots 仓库只接受 SNAPSHOT 版本。

2.6 将第三方 jar 发布到本地仓库

本节为原文整理

kaptcha(当年常用的验证码 jar)等第三方 jar 不在 Maven 中央仓库,需要手动安装到本地仓库。

# 下载 kaptcha,将 kaptcha-{version}.jar 复制到比如 C 盘根目录
mvn install:install-file \
  -Dfile=c:\kaptcha-{version}.jar \
  -DgroupId=com.google.code \
  -DartifactId=kaptcha \
  -Dversion={version} \
  -Dpackaging=jar

然后按参数在项目的 pom 中声明依赖:

<dependency>
    <groupId>com.google.code</groupId>
    <artifactId>kaptcha</artifactId>
    <version>{version}</version>
</dependency>

install:install-file 本质是调用 maven-install-plugin 提供的 install-file goal(goal 机制下面会讲)。

2.7 清理未下载完的数据

本节为原文整理

依赖下载中断后会在本地仓库留下一堆 *.lastUpdated 文件,下次 Maven 看到它会以为「这个依赖已经下过且失败了」,直接跳过。清理:

find . -name "*lastUpdated" | xargs rm -fr

或者 Windows PowerShell:

Get-ChildItem -Path ~/.m2/repository -Recurse -Filter "*.lastUpdated" | Remove-Item -Force

顺带说一句:Maven 3.8.1 之后已经能自动清理 lastUpdated 文件了,但当年还是手动时代。


三、POM 详解

pom.xml(Project Object Model)是 Maven 项目的核心。一个最小可用的 pom 长这样:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>          <!-- 组织标识 -->
    <artifactId>my-app</artifactId>        <!-- 项目标识 -->
    <version>1.0.0-SNAPSHOT</version>       <!-- 版本 -->
    <packaging>jar</packaging>              <!-- 打包类型:jar/war/pom/ear -->

    <name>My Application</name>
    <description>A simple Maven project</description>
</project>

<groupId><artifactId><version> 三者合称 GAV 坐标,是 Maven 世界的「身份证号」,任何 jar 都能用这三个值唯一定位。

3.1 继承与超级 POM

所有 pom 默认继承 super POM(超级 POM)——它定义了默认目录结构、默认中央仓库、默认插件版本。你不写任何东西时 Maven 也能跑,全靠 super POM。

自己也可以定义父 POM:

<parent>
    <groupId>com.example.parent</groupId>
    <artifactId>parent-pom</artifactId>
    <version>1.0.0</version>
    <relativePath>../parent-pom/pom.xml</relativePath>
</parent>

子 POM 会继承父 POM 的所有配置(依赖、插件、属性、构建配置),常用于「多模块项目」和「公司统一管理依赖版本」。

3.2 常用元素

元素作用
<properties>自定义变量(最常用:<java.version>1.8</java.version>
<dependencies>项目依赖
<dependencyManagement>只锁定版本,不实际引入——子模块继承后无需再写版本号
<build>构建配置(插件、资源目录、finalName)
<modules>聚合多模块(父 POM 用)
<repositories> / <pluginRepositories>自定义远程仓库 / 插件仓库
<profiles>环境隔离配置
<reporting>报告生成(覆盖率、checkstyle)
<licenses> / <organization> / <developers>项目元信息

3.3 properties 变量

<properties> 是「pom 内部的常量」:

<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.3.13.RELEASE</spring.version>
</properties>

然后用 ${变量名} 引用:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
</dependency>

好处是改一处版本号就能全项目生效。dependencyManagement 就是这个机制的标准实践。


四、依赖管理(dependency)

4.1 基本依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

4.2 scope(依赖范围):Maven 最精妙的设计之一

<scope> 决定依赖在哪个阶段可见,以及是否被打入包内。原文没提,但这是 Maven 必学:

scope编译测试运行是否打入包典型场景
compile(默认)spring-core、fastjson
testjunit、mockito
providedservlet-api(容器提供)、jsp-api
runtimeJDBC 驱动、jstl(运行时才需要)
system不走仓库,本地 jar(不推荐)
import仅在 <dependencyManagement> 中用,导入 BOM

记忆口诀:

举例当年典型的 Web 项目:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>     <!-- Tomcat 自带,部署时不打进去 -->
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
    <scope>runtime</scope>      <!-- 编译时用 JDBC 接口,运行时才加载驱动 -->
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>         <!-- 不会进入最终包 -->
</dependency>

4.3 依赖传递与冲突解决

A 依赖 B,B 依赖 C——A 项目里会自动得到 C(传递依赖)。问题是:

Maven 用两个原则解冲突:

  1. 最近优先(Nearest Wins):依赖树中深度最小的胜出。直接声明 > 一层传递 > 两层传递。
  2. 声明优先(First Declaration Wins):深度相同时,pom 中先声明的胜出

实战调试武器:

mvn dependency:tree               # 看完整依赖树
mvn dependency:tree -Dverbose     # 看冲突来源
mvn dependency:tree -Dincludes=com.google.code:kaptcha  # 只看某条路径

4.4 排除依赖(exclusions)

不希望引入传递依赖时显式排除:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>4.3.13.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

4.5 可选依赖(optional)

标记 optional=true 表示「我自己用,但你不应该传递给我下游」:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-module</artifactId>
    <version>1.0.0</version>
    <optional>true</optional>
</dependency>

五、三套生命周期

本节为原文整理

Maven 有三套相互独立的生命周期:clean、default、site。每个生命周期包含若干阶段(phase),阶段有顺序,后面的阶段依赖前面的阶段。

5.1 clean 生命周期:清理

phase作用
pre-clean清理前需要完成的工作
clean清理上一次构建生成的文件(默认删除 target/
post-clean清理后需要完成的工作

5.2 default 生命周期:构建

这是日常最常用的。重要的 phase:

phase作用
validate验证工程是否正确、所有需要的资源是否可用
compile编译项目源代码(src/main/javatarget/classes
test使用合适的单元测试框架(默认 JUnit)运行测试
package把已编译的代码打包成可发布的格式(jar/war)
integration-test如有需要,将包处理并发布到一个能进行集成测试的环境
verify运行所有检查,验证包是否有效且达到质量标准
install把包安装到 Maven 本地仓库,可被其他工程作为依赖使用
deploy在集成或发布环境下执行,将最终包拷贝到远程仓库供其他开发者共享

常见命令 vs phase 关系

  • mvn compile → 执行到 compile 阶段
  • mvn test → 执行到 test
  • mvn package → 执行到 package
  • mvn install → 执行到 install
  • mvn deploy → 执行到 deploy

你写 mvn package,Maven 会从 validate 一直执行到 package,所有上游阶段自动跑。

5.3 site 生命周期:站点

phase作用
pre-site生成站点前的工作
site生成项目站点文档
post-site生成站点后的工作
site-deploy将项目站点发布到服务器

mvn site 会在 target/site/ 输出一份 HTML 项目报告,描述项目结构、依赖、测试结果、JavaDoc 等。


六、插件与 Goal:Maven 的真正引擎

原文只讲了生命周期,没讲生命周期「如何被驱动」——这是 Maven 最重要的补完。

6.1 Phase vs Goal

Maven 的所有「真正的工作」都是插件完成的。Phase 只是一个调度点——执行到某个 phase 时,Maven 查找绑定到这个 phase 的所有插件 goal,依次调用。

6.2 内置绑定

Maven 内置了一些默认绑定,所以你执行 mvn compile 时不需要任何配置:

Phase默认绑定的 Goal
compilecompiler:compile
test-compilecompiler:testCompile
testsurefire:test
packagejar:jar(或 war:war
installinstall:install
deploydeploy:deploy

6.3 显式配置插件

大多数时候你需要指定插件版本或参数:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.7.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

6.4 必须知道的常用插件

插件关键 Goal作用
maven-compiler-plugincompiletestCompile编译 Java 源码
maven-surefire-plugintest运行单元测试(默认绑定到 test 阶段)
maven-jar-pluginjar打 jar 包
maven-war-pluginwar打 war 包
maven-resources-pluginresourcestestResources复制资源文件
maven-dependency-plugintreecopycopy-dependencies依赖分析、拷 jar 到指定目录
maven-shade-pluginshade打「uber-jar」(含全部依赖的 fat jar)
maven-clean-pluginclean清理 target
maven-deploy-plugindeploy发布到远程仓库
maven-install-plugininstallinstall-file安装到本地仓库
maven-site-pluginsite生成站点

6.5 uber-jar 实战(maven-shade-plugin)

当年要把 Spring Boot 之前的所有依赖打成一个 fat jar 部署,maven-shade-plugin 是首选:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals><goal>shade</goal></goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.example.Main</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

执行 mvn package,产出的 jar 已经包含所有依赖,可直接 java -jar xxx.jar 运行。


七、多模块项目

当年(现在也是)稍微大一点的项目都是多模块。Maven 的多模块 = 一个父 POM 聚合若干子模块

7.1 典型结构

parent-pom/
├── pom.xml                       <!-- 父 POM:packaging=pom -->
├── user-service/
│   ├── pom.xml                   <!-- 子模块:packaging=jar -->
│   └── src/...
├── order-service/
│   ├── pom.xml
│   └── src/...
└── web/
    ├── pom.xml                   <!-- 子模块:packaging=war -->
    └── src/...

7.2 父 POM 配置

<groupId>com.example</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>     <!-- 关键:父 POM 不是 jar/war,是 pom -->

<modules>
    <module>user-service</module>
    <module>order-service</module>
    <module>web</module>
</modules>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>
    </dependencies>
</dependencyManagement>

7.3 子模块 POM

<parent>
    <groupId>com.example</groupId>
    <artifactId>parent-pom</artifactId>
    <version>1.0.0</version>
</parent>
<artifactId>user-service</artifactId>
<packaging>jar</packaging>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <!-- 不写 version:从父 POM 的 dependencyManagement 继承 -->
    </dependency>
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>order-service</artifactId>
        <version>${project.version}</version>
        <!-- 引用兄弟模块:version 用 ${project.version>,自动同步 -->
    </dependency>
</dependencies>

7.4 常用命令

# 在父 POM 目录执行:构建全部模块
mvn clean install

# 只构建某个模块及其依赖
mvn -pl user-service -am clean install

# 只构建某个模块(不构建其依赖)
mvn -pl web clean package

-pl(project list)+ -am(also make dependencies)是多模块项目最常用的组合。


八、Profile:环境隔离

当年部署一个项目到 dev / test / prod 三套环境是基本操作。Profile 让 pom 的某些配置根据环境切换

8.1 在 pom 中定义

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <env>jdbc:mysql://dev-db:3306/app</env>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault>     <!-- 默认激活 -->
        </activation>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <env>jdbc:mysql://prod-db:3306/app</env>
        </properties>
    </profile>
</profiles>

激活:

mvn package -P dev          # 命令行激活
mvn package -P prod

8.2 activation 触发条件

除了手动 -P,还可以让 Maven 自动激活

<activation>
    <jdk>1.8</jdk>                                    <!-- 特定 JDK -->
    <os>
        <name>linux</name>
        <family>unix</family>
    </os>                                             <!-- 特定 OS -->
    <file>
        <exists>/etc/prod-marker</exists>
    </file>                                            <!-- 文件存在 -->
    <property>
        <name>env</name>
        <value>prod</value>
    </property>                                       <!-- -Denv=prod -->
</activation>

8.3 配合资源过滤做多环境配置

最实用的场景——一个 application.properties 里写 ${db.url},根据 profile 替换:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>     <!-- 开启过滤 -->
        </resource>
    </resources>
</build>

src/main/resources/application.properties

db.url=${db.url}
app.name=MyApp

不同 profile 的 db.url 注入不同值,构建出的 jar 里就是不同环境的配置。


九、版本管理

9.1 SNAPSHOT vs RELEASE

约定:

<version> 元素配 SNAPSHOT 后,mvn deploy 会被自动推到 <snapshotRepository>,RELEASE 则到 <repository>(参考 2.5 节)。

9.2 版本号约定

当年主流方案(Semantic Versioning):


十、Maven 命令大全

# ===== 构建生命周期 =====
mvn clean              # 清理 target/
mvn compile            # 编译主代码
mvn test-compile       # 编译测试代码
mvn test               # 运行单元测试
mvn package            # 打包(jar/war)
mvn verify             # 跑完所有检查
mvn install            # 安装到本地仓库
mvn deploy             # 发布到远程仓库
mvn site               # 生成站点
mvn clean install      # 组合:清理后重构建并安装

# ===== 依赖分析 =====
mvn dependency:list                          # 列出所有直接和传递依赖
mvn dependency:tree                          # 树形展示
mvn dependency:analyze                       # 哪些依赖声明了没用 / 用了没声明
mvn dependency:resolve                       # 解析所有依赖到本地仓库
mvn versions:display-dependency-updates      # 检查可升级版本

# ===== 调试 =====
mvn -X                                       # 开启 debug 日志
mvn -e                                       # 出错时显示完整堆栈
mvn -U                                      # 强制更新 SNAPSHOT
mvn -DskipTests                             # 跳过测试
mvn -Dmaven.test.skip=true                  # 跳过测试编译和执行
mvn -o                                      # offline 模式(只用本地仓库)
mvn -pl 模块名 -am                          # 构建指定模块及其依赖
mvn -P profile1,profile2                    # 激活多个 profile

# ===== 仓库 =====
mvn install:install-file -Dfile=xxx.jar -DgroupId=... -DartifactId=... -Dversion=... -Dpackaging=jar
mvn deploy:deploy-file -Dfile=xxx.jar ...    # 直接发布到远程仓库

# ===== 插件调用 =====
mvn help:describe -Dplugin=xxx               # 查看插件信息
mvn help:effective-pom                       # 查看当前项目的「有效 POM」(合并父 POM 后)

十一、错误排查

11.1 依赖下载失败

症状Could not resolve dependencies for project ...

思路

  1. 配镜像(参考 2.3 节)
  2. 清理 *.lastUpdated(参考 2.7 节)
  3. ~/.m2/repository/... 对应目录后重下
  4. 检查 <repository><releases> / <snapshots> 是否启用

11.2 版本冲突

症状NoSuchMethodError / ClassNotFoundException / LinkageError,且发生在运行时

思路

mvn dependency:tree -Dverbose | grep -i '冲突的类名'

找到后用 <exclusion> 排除多余版本,或在 <dependencyManagement> 中强制锁定。

11.3 编译错误

症状[ERROR] BUILD FAILURE + 编译报错

思路

  1. 检查 JDK 版本(mvn -v 看 Maven 用的 JDK)
  2. 检查 maven-compiler-pluginsource / target 配置
  3. 看一下「编译错」是不是某个传递依赖爆出来的(升级或排除该依赖)

11.4 插件找不到

症状No plugin found for prefix 'xxx'

思路

  1. 检查 <pluginRepositories> 是否配了
  2. 镜像是否覆盖了 plugin 仓库(mirrorOf 不能是 *,!some-plugin-repo 之类)
  3. 直接写完整 GAV(<groupId>:<artifactId>:<version>)而不是简写

十二、m2eclipse 集成

本节为原文整理

m2eclipse 是 Eclipse 的 Maven 插件(2017 年 IntelliJ IDEA 已自带 Maven 支持,但 Eclipse 用户还很多,所以保留这一节)。

12.1 预置的 mvn 命令

在 Eclipse 中右键 Maven 项目或 pom.xml 文件 → Run As,可以看到预置的 mvn 命令列表(如 mvn testmvn install 等)。

12.2 自定义 mvn 命令

Run As 菜单选择 Maven Build…,输入自定义命令如 mvn clean install,点 OK。

下次右键 → Run AsMaven Build 就能看到这条自定义命令。

当年还有用 MyEclipse 的,但 MyEclipse 的 Maven 集成体验比 Eclipse + m2eclipse 差很多,统一建议用后者。


十三、实战:30 分钟搭一个企业级多模块 Web 项目骨架

学完上面 12 节,用一个例子串起来。目标:一个父 POM + user-service(jar)+ web(war)。

13.1 目录结构

company-pom/
├── pom.xml
├── user-service/
│   ├── pom.xml
│   └── src/main/java/com/example/user/User.java
└── web/
    ├── pom.xml
    └── src/main/webapp/WEB-INF/web.xml

13.2 父 POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>company-pom</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.3.13.RELEASE</spring.version>
        <junit.version>4.12</junit.version>
    </properties>

    <modules>
        <module>user-service</module>
        <module>web</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

13.3 user-service 子模块

user-service/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>company-pom</artifactId>
        <version>1.0.0</version>
    </parent>
    <artifactId>user-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
</project>

user-service/src/main/java/com/example/user/User.java

package com.example.user;

public class User {
    private Long id;
    private String name;

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

13.4 web 子模块

web/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>company-pom</artifactId>
        <version>1.0.0</version>
    </parent>
    <artifactId>web</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>user-service</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
</project>

13.5 构建

company-pom 目录执行:

mvn clean install

会按 user-serviceweb 的顺序构建,user-service 打成 jar 安装到本地仓库,web 模块再把这个 jar 拷进 WEB-INF/lib/ 打成 war。

访问 target/web.war

cp web/target/web.war $TOMCAT_HOME/webapps/
$TOMCAT_HOME/bin/startup.sh

十四、写给 2017 年读完这篇的你

那年 Spring 4.x 还在收尾,Spring 5.0(基于 Java 8 + Reactor)即将发布;Jenkins + Maven 是事实上的 CI;Nexus 2.x 是大多数公司的私服;Tomcat 8 是 Web 容器主流;IDE 还是 Eclipse / IDEA 之争。

掌握这篇讲的东西,足够你在中等规模的 Java 项目里游刃有余:

Maven 的世界后续演化出 Gradle(在 Android 圈几乎一统)、SBT(在 Scala 圈)——但 Maven 仍然是 Java 企业级项目的事实标准,特别是和 Jenkins、Sonar、Nexus 一起构成的「传统企业 CI/CD 黄金组合」。

下一篇开始,会基于这个基础聊一些更深入的话题(多模块的依赖最佳实践、私服搭建、版本发布流程)。本篇先到这里。


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.Linux 命令大全:SSH 免登录、查找、防火墙与运维必会命令
  2. 02.Maven 入门到精通:依赖管理、生命周期与多模块项目
  3. 03.Nginx:高性能原理与常用配置(负载均衡 / 跨域 / 防盗链)
  4. 04.Maxwell 数据初始化:maxwell-bootstrap 全量同步实践
  5. 05.mac 系统的 IntelliJ IDEA 快捷键大全

上一篇
mac 系统的 IntelliJ IDEA 快捷键大全
下一篇
Git 知识系列(六):仓库维护与排错