Maven高效实践(上)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
日志服务 SLS,月写入数据量 50GB 1个月
简介: 从Hello world说起,Maven是一个项目管理工具,在安装好并配置系统环境变量后,在终端执行:mvn -version首先约定好开发工具为IDEA,在IDEA中配置好Maven,怎么配置就不多说了。


一、从Hello world说起



Maven是一个项目管理工具,在安装好并配置系统环境变量后,在终端执行:mvn -version,保证输出以下内容:

Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T02:33:14+08:00)
Maven home: /usr/local/soft/maven
Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/local/soft/jdk8/jre
Default locale: zh_CN, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-29deepin-generic", arch: "amd64", family: "unix"

首先约定好开发工具为IDEA,在IDEA中配置好Maven,怎么配置就不多说了。


1. 创建项目


打开IDEA,点击菜单File -> New -> Project...,然后在左侧选择MavenNext,填写好GAV,然后填写好项目路径等创建完成。

GAV:是groupId、artifactId、version的缩写。

groupId:一般是公司的域名倒置,artifactId:项目名称或模块名称,version:版本号

打开项目会在IDEA左侧看到Maven的目录结构,打开pom.xml文件,会看到下面信息:

<?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.ajn</groupId>
    <artifactId>spring-boot</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</project>


2. 添加依赖


首先添加spring相关父pom配置,我个人不建议使用spring-boot-starter-parent而使用spring-boot-dependencies,我们只需要继承父pom的依赖,不需要其他配置,项目的掌控权要牢牢握在自己手里。然后添加spring-boot-starter-web依赖。

<?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>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>
    <groupId>com.ajn</groupId>
    <artifactId>spring-boot</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>
3. 添加插件


3. 添加插件


添加完插件和部分配置属性,我们可以看到添加的插件有:

  • maven-compiler-plugin:编译
  • maven-jar-plugin:打包
  • maven-assembly-plugin:自定义打包
  • maven-source-plugin:打包源码
  • maven-release-plugin:版本发布

Maven是基于微核心插件化思想设计的,功能全部体现在插件上,我们在后面详细讲到。还添加的属性有:

  • project.build.sourceEncoding:编码
  • project.reporting.outputEncoding:编码
  • java.version:JDK版本
  • maven.test.skip:跳过测试
  • maven.install.skip:跳过安装到本地仓库
  • maven.deploy.skip:跳过发布到远程仓库(私服)

一般Web项目跳过 install 和 deploy,因为它不需要被别人依赖。需要被别人依赖的API项目,都应将 Jar 包和源码包发布到私服,供调用方使用。

Profile可以用来区分环境,具体用法以后说,添加的Profile有:

  • test:表示测试环境
  • prod:表示生产环境

到这差不多pom的配置就完成了,除了以后添加依赖。

<?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>org.springframework.boot</groupId>
       <artifactId>spring-boot-dependencies</artifactId>
       <version>2.0.4.RELEASE</version>
   </parent>
   <groupId>com.ajn</groupId>
   <artifactId>spring-boot</artifactId>
   <version>1.0.0-SNAPSHOT</version>
   <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
       <java.version>1.8</java.version>
       <maven.test.skip>true</maven.test.skip>
   </properties>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
   </dependencies>
   <build>
       <finalName>${project.name}</finalName>
       <resources>
           <resource>
               <directory>src/main/java</directory>
               <includes>
                   <include>**/*.xml</include>
               </includes>
           </resource>
           <resource>
               <directory>src/main/resources</directory>
               <includes>
                   <include>*</include>
               </includes>
               <filtering>true</filtering>
           </resource>
       </resources>
       <plugins>
           <plugin>
               <artifactId>maven-compiler-plugin</artifactId>
               <configuration>
                   <source>${java.version}</source>
                   <target>${java.version}</target>
               </configuration>
           </plugin>
           <plugin>
               <artifactId>maven-jar-plugin</artifactId>
               <configuration>
                   <archive>
                       <addMavenDescriptor>false</addMavenDescriptor>
                       <manifest>
                           <addClasspath>true</addClasspath>
                           <classpathPrefix>lib/</classpathPrefix>
                           <mainClass>com.ajn.spring.boot.Application</mainClass>
                       </manifest>
                   </archive>
               </configuration>
           </plugin>
           <plugin>
               <artifactId>maven-assembly-plugin</artifactId>
               <configuration>
                   <descriptors>
                       <descriptor>src/main/config/package.xml</descriptor>
                   </descriptors>
               </configuration>
               <executions>
                   <execution>
                       <id>mvn-assembly</id>
                       <phase>package</phase>
                       <goals>
                           <goal>single</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
           <plugin>
               <artifactId>maven-source-plugin</artifactId>
               <executions>
                   <execution>
                       <id>attach-sources</id>
                       <goals>
                           <goal>jar-no-fork</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-release-plugin</artifactId>
           </plugin>
       </plugins>
   </build>
   <profiles>
       <profile>
           <id>test</id>
           <properties>
               <profile.active>test</profile.active>
           </properties>
       </profile>
       <profile>
           <id>prod</id>
           <properties>
               <profile.active>prod</profile.active>
           </properties>
       </profile>
   </profiles>
</project>

添加assembly插件打包配置,根据maven-assembly-plugin插件配置的文件路径,在main下新建文件夹config,然后新建文件package.xml,插件配置用法后面会说到。

package com.ajn.spring.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args); 
    }
}

每个项目都可以有自己定制的打包方式,并不拘泥于某类插件。


4. 添加代码


到这里我们项目已经搭建好了并添加Springboot的依赖,就可以直接写代码启动测试。在java目录下创建包com.ajn.spring.boot,在包下新建类Application.java

package com.ajn.spring.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args); 
    }
}

在包com.ajn.spring.boot下新建包controller,在包下新建类HelloController.java

package com.ajn.spring.boot.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("hello")
public class HelloController {
    @GetMapping
    public String hello() {
        return "Hello world";
    } 
}

运行主方法来启动项目,输出日志中最后一行如下表示启动成功。

2018-11-28 20:23:00.754 INFO 22671 --- [main] com.ajn.spring.boot.Application: Started Application in 1.296 seconds (JVM running for 1.64)

然后用浏览器访问 http://localhost:8080/hello,返回:

Hello world

到这里我们只是先把项目跑起来,在Maven中配置的插件也似乎没有用到,后面会深入讲解Maven是如何管理和构建项目的。


二、Maven



Apache Maven的定义:Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后Maven可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。

它运用的设计思想:

  • 继承思想:子pom可以继承父pom的配置和属性。
  • 仓库思想:本地仓库、远程仓库(私服)
  • 约定大于配置:功能配置和指令约定俗成
  • 微核心插件化:本身安装包核心不大,插件也像依赖一样可由仓库管理


1. 目录结构


Maven的基本目录结构,不管是自动生成,还是自己手动创建,都需按照这种格式,当然你可以自定义,但不建议这样做,会增加更多没必要的配置。

spring-boot
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│       └── java
│       └── resources
└── target
  • spring-boot:项目根目录
  • src/main/java:源代码目录
  • src/main/resources:资源文件目录,默认打包至类路径(classpath)下
  • src/test/java:测试源代码目录
  • src/test/resources:测试资源文件目录
  • pom.xml:(Project Object Model),项目对象模型配置文件


2. pom

Maven把Java项目抽象成一个项目对象模型(Project Object Model),而pom.xml配置就是这抽象过程的体现。它不仅能把源代码编译成字节码,这些配置还包含:项目的描述信息,项目唯一的坐标,描述项目的属性,依赖的其他项目等等。


2.1 配置详解

2.1.1 基本配置

  • modelVersion:pom模型版本,maven2只能为4.0.0
  • groupId:项目组编号
  • artifactId:项目编号
  • version:版本号
  • packaging:打包方式,有pom,jar,war,maven-plugin等等,缺省值:jar
  • properties:定义常量属性,可以在pom其他地方通过${}来引用
  • name:项目名,Maven产生的文档使用
  • description:项目描述
  • url:主页URL,maven文档中保存
  • developers:开发人员
  • contributors:其他贡献人员


2.1.2 依赖配置

  • parent:项目继承中指定父项目,子节点为GAV1
  • modules:项目聚合中指定子模块项目,子节点为module
  • dependencies:依赖配置,需提供引用的依赖的GAV,可以从父项目继承
  • dependencyManagement:依赖管理配置,子节点配置dependencies,管理子项目依赖


2.1.3 构建配置

  • build:构建配置
  • finalName:build目标文件的名称,缺省值:${artifactId}-${version}
  • resources:资源拷贝插件配置
  • plugins:插件配置
  • filters:定义过滤的.properties文件,该文件的配置会应用到resources
  • reporting:发布配置
  • profiles:配置文件组,可以配置多种配置,按需激活其中一种


2.1.4 仓库配置

  • repositories:配置获取依赖的仓库,一般配置私服中的中央仓库,缺省值:Maven中央仓库
  • pluginRepositories:配置获取插件的仓库,和repositories意思一样
  • distributionManagement:发布管理,可以配置依赖发布的仓库
  • scm:软件配置管理,可以配置web站点和CVS


3. 依赖


3.1 依赖范围(scope)

3.1.1 compile

编译范围:scope配置的缺省值,编译范围依赖在所有的classpath中可用,同时也会被打包。

3.1.2 provided

已提供范围:该类型依赖只有在当JDK或者一个容器已提供该依赖之后才使用,已提供范围的依赖在编译classpath(不是运行时)可用,它们不是传递性的,也不会被打包。

3.1.3 runtime

运行时范围:该类型依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如:你可能在编译的时候只需要JDBC API jar,而只有在运行的时候才需要JDBC驱动实现。

3.1.4 test

测试范围:该类型依赖在一般的编译和运行时都不需要,只有在测试编译和测试运行阶段可用。

3.1.5 system

系统范围:该依赖范围与provided类似,但是必须显式的提供一个对于本地系统中jar文件的路径。这么做是为了允许基于本地对象编译,而这些对象是系统类库的一部分。


3.2 传递性依赖

一个传递性依赖就是对于一个依赖的依赖。如果project-a依赖于project-b,后者接着依赖于project-c,那么project-c就被认为是project-a的传递性依赖。如果project-c依赖于project-d,那么project-d就也被认为是project-a的传递性依赖。


3.3 冲突解决(exclusions)

有很多时候你需要排除一个传递性依赖,比如当你依赖于一个项目,后者又继而依赖于另外一个项目,但你的希望是,要么整个的排除这个传递性依赖,要么用另外一个提供同样功能的依赖来替代这个传递性依赖。比如:

<dependency>
    <groupId>org.sonatype.mavenbook</groupId>
    <artifactId>project-a</artifactId>
    <version>1.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.sonatype.mavenbook</groupId>
            <artifactId>project-b</artifactId>
        </exclusion>
    </exclusions>
</dependency>


3.4 依赖管理(dependencyManagement)


在一个系统中有很多相互关联个Maven项目,如果每一个使用如MySQL数据库驱动依赖的项目都需要独立的列出该依赖的版本,在你需要升级到一个新版本的时候你就会遇到问题。由于这些版本号分散在你的项目树中,你需要手工的编写每一个引用该依赖的pom.xml,确保每个地方的版本号都更改了。此时我们可以在父项目中使用dependencyManagement来集中管理依赖。比如:

<!-- 父项目 -->
<project>
    [...]
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.2</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    [...]
</project>
<!-- 子项目 -->
<project>
    [...]
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
    [...]
</project>

4. 生命周期

在前面Maven的定义中我们说到它是运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑,一个生命周期是一些阶段的序列,一个生命周期阶绑定了零个或多个目标(goal)。我们可以使用mvn指令直接运行生命周期和目标。

  • 运行生命周期:mvn clean, mvn package, mvn clean package等等
  • 运行插件目标:mvn clean:clean, mvn resources:resources等等

在项目根目录下我们可以直接在终端运行mvn clean,如果一个项目自己配置了多个pom.xml文件,需要指定执行的pom,使用mvn clean -f pomTest.xml


4.1 clean

  • phases: clean
  • goals: clean:clean

该生命周期用于移除你项目构建的文件,默认是删除 project.build.directory, project.build.outputDirectory, project.build.testOutputDirectoryproject.reporting.outputDirectory目录。


4.2 compile

  • phases: process-resources -> compile
  • goals: resources:resources -> compiler:compile

该生命周期用于编译源码和测试源码,默认编译sourcetarget的JDK配置是1.6,如果想使用更高版本,需要自己手动配置。


4.3 package

  • phases: process-resources -> compile -> process-classes -> process-test-resources -> test-compile -> test -> prepare-package -> package
  • goals: resources:resources -> compiler:compile -> resources:testResources -> compiler:testCompile -> surefire:test ->jar:jar

该生命周期用于项目打包,默认打的是jar类型。


4.4 install

  • phases: process-resources -> compile -> process-classes -> process-test-resources -> test-compile -> test -> prepare-package -> package -> pre-integration-test -> integration-test -> post-integration-test -> verify -> install
  • goals: resources:resources -> compiler:compile -> resources:testResources -> compiler:testCompile -> surefire:test -> jar:jar -> install:install

该生命周期用于将你打好的包安装到本地仓库,会根据项目的GAV来确定它在仓库中的路径。本地仓库目录可以在Maven的配置文件setting.xml中的节点配置,缺省值:~/.m2/repository

打包执行顺序图:

微信图片1.jpg

打包日志分析:

[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< com.ajn.springboot:study-springboot >-----------------
[INFO] Building study-springboot 1.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ study-springboot ---
[INFO] Using 'UTF-8'encoding to copy filtered resources.
[INFO] Copying 2resources
[INFO] Copying 3resources
[INFO] Copying 1resource
[INFO]
[INFO] --- maven-compiler-plugin:3.7.0:compile (default-compile) @ study-springboot ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 32source files to /home/ajn/Code/study/study-springboot/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ study-springboot ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.7.0:testCompile (default-testCompile) @ study-springboot ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.21.0:test (default-test) @ study-springboot ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ study-springboot ---
[INFO] Building jar: /home/ajn/Code/study/study-springboot/target/study-springboot.jar
[INFO]
[INFO] --- maven-assembly-plugin:3.1.0:single (mvn-assembly) @ study-springboot ---
[INFO] Reading assembly descriptor: src/main/config/package.xml
[INFO] Building zip: /home/ajn/Code/study/study-springboot/target/study-springboot-bin.zip
[INFO]
[INFO] --- maven-source-plugin:3.0.1:jar-no-fork (attach-sources) @ study-springboot ---
[INFO] Building jar: /home/ajn/Code/study/study-springboot/target/study-springboot-sources.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ study-springboot ---
[INFO] Installing /home/ajn/Code/study/study-springboot/target/study-springboot.jar to /home/ajn/.m2/repository/com/ajn/springboot/study-springboot/1.0.1-SNAPSHOT/study-springboot-1.0.1-SNAPSHOT.jar
[INFO] Installing /home/ajn/Code/study/study-springboot/pom.xml to /home/ajn/.m2/repository/com/ajn/springboot/study-springboot/1.0.1-SNAPSHOT/study-springboot-1.0.1-SNAPSHOT.pom
[INFO] Installing /home/ajn/Code/study/study-springboot/target/study-springboot-bin.zip to /home/ajn/.m2/repository/com/ajn/springboot/study-springboot/1.0.1-SNAPSHOT/study-springboot-1.0.1-SNAPSHOT-bin.zip
[INFO] Installing /home/ajn/Code/study/study-springboot/target/study-springboot-sources.jar to /home/ajn/.m2/repository/com/ajn/springboot/study-springboot/1.0.1-SNAPSHOT/study-springboot-1.0.1-SNAPSHOT-sources.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.022s
[INFO] Finished at: 2018-12-02T22:50:31+08:00
[INFO] ------------------------------------------------------------------------


上面为我们前面搭建好的项目执行mvn install时打印的日志,从日志中可以看出执行的goal,完全是按照默认或者我们配置的生命周期阶段的顺序,比如:配置的maven-assembly-plugin打包插件,执行的目标(goal)是single,绑定的生命周期阶段(phase)是package,所以在执行目标jar:jar的后面。在pom中配置跳过测试true

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
Java Linux Apache
Java maven反应堆构建学习实践
Java maven反应堆构建学习实践
130 0
|
Java Maven C++
Maven高效实践(下)
5. 插件 6. Profiles 7. 仓库 8. 实例
206 0
|
自然语言处理 IDE Dubbo
通过IDE/Maven 部署 Serverless 应用实践|学习笔记
快速学习 通过 IDE/Maven 部署 Serverless 应用实践
156 0
|
Java Maven Android开发
Android 多模块(lib)批量打包 aar 上传 maven 仓库实践
大型项目模块众多时,可配合 gradle.properties 和 settings.gradle 设置开发中依赖 aar 库替换本地模块 从而加快编译速度。publishmaven.sh 还可添加到 ci 任务实现自动打包。
|
Java Apache Maven
(二)Java工程化--Maven实践
Maven项目版本号 默认版本号: 1.0-SNAPSHOT 最佳实践是约定该版本为不稳定版本,如果发布一定要删除; 建议的版本规则: 主版本号.次版本号.增量版本号- 如:1.0.0-RELEASE 10.
1299 0
|
28天前
|
Java Maven
maven项目的pom.xml文件常用标签使用介绍
第四届人文,智慧教育与服务管理国际学术会议(HWESM 2025) 2025 4th International Conference on Humanities, Wisdom Education and Service Management
84 8
|
26天前
|
Java 应用服务中间件 Maven
Maven的三种项目打包方式——pom,jar,war的区别
Maven 提供了多种打包方式,分别适用于不同类型的项目。pom 用于父项目或聚合项目,便于项目的结构和依赖管理;jar 用于Java类库或可执行的Java应用程序;war 则专用于Java Web应用程序的部署。理解这些打包方式的用途和特点,可以帮助开发者更好地配置和管理Maven项目,确保构建和部署过程的顺利进行。无论是单模块项目还是多模块项目,选择合适的打包方式对于项目的成功至关重要。
66 3