Java环境初始化 & Maven版本安装工具使用

AI 摘要: 本文介绍了Java编译语言、JVM虚拟机以及Maven的模块安装过程

非Java的同学,快速时间实践了下Maven的模块安装过程

1. Java环境部署

1.1. Java开发与运行环境

Java介于编译语言与解释语言之间,通过编译器将Java代码编译成"java字节码"(抽象了CPU指令)在JVM虚拟机上运行,以实现跨平台的能力; JVM虚拟机兼容性好,支持低版本字节码在高版本JVM上运行。

  • 开发标准
    • Java ME: Java SE的Mini版,不与JSE版本兼容
    • Java SE:标准版,包括Java标准库、语言本身
    • Java EE:企业版,增加大量API和库,方便Web开发,JEE使用的JVM与JSE兼容(Spring框架),Java 其他应用:安卓开发、Hadoop、Spark、Flink等
  • 开发与运行环境
    • JDK(Java Development Kit): Java开发工具包,提供JRE(Java Running Env)环境,包括
    • JRE(Java Runtime Environment): Java运行环境
  • 2020/09 Java版本是Java15
  • BIN执行程序
    • java: 启动JVM,运行Java字节码
    • javac: Java字节码编译器,将.java源文件编译成.class结尾的字节码
    • jar: 打包程序,将多个.class字节码文件,打成jar包程序
    • javadoc:从代码生成文档
    • jdb: Java调试器,开发阶段运行调试

1.2. JDK下载和配置

  1. JDK下载: https://www.oracle.com/java/technologies/javase-jdk15-downloads.html
  2. 配置~/.zshrc
1
2
export JAVA_HOME=`/usr/libexec/java_home -v 15`
export PATH=$JAVA_HOME/bin:$PATH

2. JAVA程序

2.1. HelloWorld

  1. javac编译Hello.java => Hello.class(java字节码)
  2. java Hello,寻找Hello.class => 启动JVM,执行Hello.class;另外,从java11其,支持java运行Hello.java,即java Hello.Hello
1
2
3
4
5
6
7
8
// Hello.java文件,与定义类名一致,则java Hello可以执行
// 从Java11起,文件名如果不一致,则需要指定Java源文件全称, Hello.java,即java Hello.java
public class Hello {
    // java固定入口程序
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

2.2. Java语法

  • 基本语法
    • 变量类型(整、浮、布、字符+串、数组)
    • 流程控制(IO、IF、swith、while、do while、for、break、continue)
    • 数组操作(多维、遍历、排序)
  • OOP
    • 构造方法(与类名相关)
    • 方法
    • 可见范围(私有、保护、公共)
    • 继承:
      • 超类、父类、基类;
      • 子类、扩展类。
      • Java仅支持单集成。Object特殊类。
      • final阻止继承。
      • 集成向下转换(父转子OK,子转父Err)。
      • instanceof判断实例类型
    • 重载(Overload):方法名称一样,但参数不一样。重载的返回值类型通常都是相同的。
      • int indexOf(int ch):根据字符的Unicode码查找;
      • int indexOf(String str):根据字符串查找;
    • 覆写(Override):方法签名一致,加上@Override可以让编译器帮助检查是否进行了正确的覆写(理解为编译器校对)
    • 多态:类似接口,或者基类作为参数,可以传入不同子类,产生不同效果
    • 接口:接口做抽象,多态发挥能力。interface
    • 抽象:
      • 抽象方法:public abstract void run();
      • 抽象类:`abstract class Person {},抽象方法,有抽象方法的类就是抽象类
    • 静态字段与静态方法:
      • static字段: 也叫实例字段,所有实例共享实例字段空间
      • static方法: 无需实例类,就可以调用类方法

2.3. package包

  • 通过package包解决命名冲突,比如package a,多层包用点号分隔,包没有父子关系:package a.b.c
  • 通常包放在src目录内;在同一个包下的,文件共用该包作用域,除此外还受可见性修饰词控制(public、protect、private

import导入包: 直接通过全路径包名+类名+方法调用,比如a.b.ClassX.Func()a.b包下的ClassX类的Func()方法,问题是会导致非常多冗余

  • 通过import导入包,再通过类名+方法名调用
    • import a.b.ClassX,再调用Func()
    • 支持*批量导入,导入所有的class,不包括子class`
  • 包名推荐倒置域名方式,Eg由大到小com.tencent.ketang.sample
 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
// 包路径
com
├── ali
│   ├── AliHello.java
│   └── Hello.java
├── tencent
│   └── Hello.java
└── util
    └── UtilHello.java

// 编译所有java文件
$ javac com/ali/* com/tencent/* 

// 通过import导入外部包
package com.util;

import com.ali.AliHello;
import com.tencent.*;

public class UtilHello {
    public static void main(String[] args) {
        AliHello.SayHello();
        Hello.main(args);
    }
}

2.4. classpath

classpath是JVM用到的一个字节码class文件path路径变量,即一组目录的集合,用来指示JVM如何搜索class字节码文件文件。

classpath设置

  • 通过环境变量设置(不推荐):/usr/shared:/usr/local/bin:PathA:PathB
  • 通过JVM启动设置(推荐): -classpath,或简写-cp 示例:java -cp /usr/shared:/usr/local/bin:PathA:PathB

默认classpath为当前目录,若package包名com.example和目录名com > example > Hello.java约定一致,只可以直接使用编译,java -cp . com.example.Hello

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ tree ./com 
./com
├── ali
│   ├── Hello.class
│   └── Hello.java      // package com.ali;
└── tencent
    ├── Hello.class
    └── Hello.java      // package com.tencent;

// 编译java代码
$ javac com/ali/* com/tencent/*

// 运行java字节码程序,甚至可以省略-cp指定,因为默认cp就是当前目录
$ java -cp . com.tencent.Hello
$ java com.ali.Hello        
learn.Hello, QQ!

2.5. jar包

  • jar包实际上就是一个zip格式的压缩文件,可以通过zip压缩+重命名得到jar包,但需要注意jar包目录结构
  • jar不管class之间的依赖,Java9开始主要解决依赖问题,若依赖有问题通常遇到ClassNotFoundException错误
  • 可以Maven便创建和管理jar包

zip打包jar,并运行jar包

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 目录
com
├── ali
│   ├── Hello.class
│   └── Hello.java
└── tencent
    ├── Hello.class
    └── Hello.java

// 通过zip打包jar文件
$ zip -r com.zip ./com
$ mv ./com.zip ./com.jar

// 运行jar包,可以将./com.jar拷贝到任何地方,然后执行下面命令
$ java -cp . com.ali.Hello
learn.Hello, ALIBABA!
$ java -cp ./com.jar com.ali.Hello
learn.Hello, ALIBABA!
$ java com.ali.Hello                  
learn.Hello, ALIBABA!

利用jar命令打包

  • 创建:jar --carete --file xx.jar ...,简写成jar -c -f xx.jar ...,不支持连写,即-cf xx.jar报错
  • 更新:jar --update --file xx.jar ...,简写成jar -u -f xx.jar ...

被打包内容支持:

  • 通过.class结尾的字节码文件
  • 通过manifest申明的文件清单,通过-C dir指定包含class文件目录
  • 通过Module模块方式申明,通过-C dir指定包含class文件目录
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 // 创建包含两个类文件的名为 classes.jar 的档案:
 jar --create --file classes.jar Foo.class Bar.class
 
 # 使用现有的清单创建档案, 其中包含 foo/ 中的所有文件:
 jar --create --file classes.jar --manifest mymanifest -C foo/ .

 # 创建模块化 jar 档案, 其中模块描述符位于
 # classes/module-info.class:
 jar --create --file foo.jar --main-class com.foo.Main --module-version 1.0
     -C foo/ classes resources

 # 将现有的非模块化 jar 更新为模块化 jar:
 jar --update --file foo.jar --main-class com.foo.Main --module-version 1.0
     -C foo/ module-info.class

 # 创建包含多个发行版的 jar, 并将一些文件放在 META-INF/versions/9 目录中:
 jar --create --file mr.jar -C foo classes --release 9 -C foo9 classes

2.6. Module模块

jar是class的封装,jmod是用于解决模块依赖和拆分问题,jre是应用运行时环境,直接可执行,可以通过jlink链接指定mod降低jre生成目录的大小

Java Module背景 从java9开始引入模块,.classJVM可见最小执行文件,jar将离散.class管理起来,如果有多个jar依赖包,需要将多个jar包引入后执行:java -cp a.jar:b.jar:c.jar:com.tencent.sample.jar com.tencent.sample.Main

java9开始将原有的Java标准库由rt.jar单个jar包分拆成了几十个以.jmod结尾的模块,放在$JAVA_HOME/jmods目录下, 模块名就是文件名,比如模块java.base对应的文件就是java.base.jmod,所有模块都依赖java.base根模块。

与把一堆class字节码文件打包jar包相比,模块在此基础上还解决了模块依赖关系问题

1
2
3
4
5
6
7
8
$ ls -l $JAVA_HOME/jmods
total 154512
-rw-r--r--  1 root  wheel  17874844  9 15 22:11 java.base.jmod
-rw-r--r--  1 root  wheel    124407  9 15 22:11 java.compiler.jmod
-rw-r--r--  1 root  wheel     51506  9 15 22:11 java.datatransfer.jmod
-rw-r--r--  1 root  wheel  13349032  9 15 22:11 java.desktop.jmod
-rw-r--r--  1 root  wheel     40729  9 15 22:11 java.instrument.jmod
...

如何编写模块

  1. 初始化模块目录结构,创建binsrc两个目录,src内放java包源码
  2. 编写java模块源码
  3. 编写module-info.java文件,指明源码模块依赖
  4. 编译,会在bin下生产了class后缀的字节码文件,以及多出一个module-info.class文件
  5. 运行,通过java -cp指定classpath,指定入口包的类名(com.ali.Hellocom.tencent.Hello)
  6. 打包模块目录为单个jar文件,同时申明入口类,jar -c -f ali-hello.jar -e com.ali.Hello -C ./bin .
  7. 运行,通过jar -jar运行jar包
  8. 创建模块,同jmod create --class-path ali-hello.jar ali-hello.jmod生成模块文件

注意:java相关运行命令参数有的不支持简写,比如java -cpjava --class-path一致,但jmod仅支持--class-path,不支持简写的-cp

2.6.1. 初始化模块目录结构,创建bin、src

 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
// 初始化模块目录结构
$ tree oop-base 
oop-base
├── bin   
└── src
    ├── com
    │   ├── ali
    │   │   ├── AliHello.java
    │   │   └── Hello.java
    │   └── tencent
    │       └── Hello.java
    └── module-info.java

// 1.1 com.ali.AliHello.java
package com.ali;

public class AliHello {
    public static void SayHello() {
        System.out.println("Hey, Ali");
    }
}
// 1.2 com.ali.Hello.java (入口文件)
package com.ali;

public class Hello {
    public static void main(String[] args) {
        AliHello.SayHello();
    }
}
// 2 com.tencent.Hello (入口文件)
package com.tencent;

public class Hello {
    public static void main(String[] args) {
        System.out.println("oop-base module, Hello, QQ!");
    }
}

// module-info.java内容,假定java包依赖xml、logging模块内容,则可以编译如下
module oop.base {
    requires java.base;
    requires java.xml;
    requires java.logging;
}


// 模块内的类遵循public,protect,private可见性约束,但如果想把oop.base模块内某些类导出,则需要通过expose导出包
module oop.base {
    // 假定需要导出com.ali包下所有内容给到其他模块使用
    exports com.ali;

    requires java.base;
    requires java.xml;
    requires java.logging;
}

2.6.2. 编译java源码包

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 编译,指定module-info.java位置、class源码内容,-d指定编译后字节码文件存储位置
$ javac -d bin src/module-info.java src/com/ali/* src/com/tencent/*

// 编译后,看到bin下生产了class后缀的字节码文件,以及多出一个module-info.class文件
$ tree oop-base 
oop-base
├── bin
│   ├── com
│   │   ├── ali
│   │   │   ├── AliHello.class
│   │   │   └── Hello.class
│   │   └── tencent
│   │       └── Hello.class
│   └── module-info.class
├── oop-base.iml
└── src
    ├── com
    │   ├── ali
    │   │   ├── AliHello.java
    │   │   └── Hello.java
    │   └── tencent
    │       └── Hello.java
    └── module-info.java

2.6.3. 基于classpath目录运行

1
2
3
4
5
6
// 指定cp目录,运行一把
$ cd oop-base
$ java -cp ./bin com.ali.Hello   
Hey, Ali
$ java -cp ./bin com.tencent.Hello 
oop-base module, Hello, QQ!

2.6.4. 将bin目录下class字节码打包成jar,并运行jar包

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 将bin目录下class字节码打包成jar,需要指定--main-class
$ jar --create --file ali-hello.jar --main-class com.ali.Hello  -C bin/ .
$ jar -c -f tencent-hello.jar -e com.tencent.Hello -C ./bin .

// 指定jar文件,运行一把(因为ali-hello.jar和tencent-hello.jar都有彼此的入口类,所以jar包可以相互执行)
$ java -cp ./ali-hello.jar  com.ali.Hello 
Hey, Ali
$ java -cp ./ali-hello.jar  com.tencent.Hello
oop-base module, Hello, QQ!
$ java -cp ./tencent-hello.jar com.ali.Hello 
Hey, Ali
$ java -cp ./tencent-hello.jar com.tencent.Hello 
oop-base module, Hello, QQ!

// 通过-jar指定jar文件,默认启动入口程序
$ java -jar ali-hello.jar        
Hey, Ali
$ java -jar tencent-hello.jar 
oop-base module, Hello, QQ!

// 因为jar文件已是模块化了的,因此可以通过--module运行已被jar打包的模块
$ java --module-path tencent-hello.jar --module oop.base
oop-base module, Hello, QQ!

2.6.5. 通过jmod打包

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 打包成jmod
$ jmod create --class-path ali-hello.jar ali-hello.jmod

// jmod明细信息
$ jmod describe ali-hello.jmod 
oop.base
requires java.base
requires java.logging
requires java.xml
contains com.ali
contains com.tencent
main-class com.ali.Hello

// jmod包含内容
$ jmod list ali-hello.jmod 
classes/module-info.class
classes/META-INF/MANIFEST.MF
classes/com/tencent/Hello.class
classes/com/ali/AliHello.class
classes/com/ali/Hello.class

2.6.6. 通过jlink链接依赖mod,并打包生成JRE目录

之前说到rt.jar基本jar包过大,过去运行Java应用,必须下载完整的JRE运行时环境(非常大100M),可以通过jlink指定我们应用的模块依赖,给JRE内容瘦身,瘦身完后,只需要把Jre目录打包发给对方,对方直接运行即可;

1
2
3
4
5
$ jlink --module-path ./ali-hello.jmod --add-modules java.base,java.xml,oop.base --output jre/

// 生成后,可以直接执行JRE下的java程序
$ ./jre/bin/java --module oop.base
Hey, Ali

2.7. 类之间嵌入

  • 类之间关系:
    • 没有父子关系,独立;
    • 嵌入类
  • Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种

3. Maven

  1. 下载安装Maven依赖包管理工具
  2. ~/.m2配置目录,配置Maven镜像仓库,加速依赖包下载速度
  3. 初始化Maven项目结构,配置pom.xml依赖包管理文件
  4. 进入pom.xml配置目录,执行相关mvn命令:清理、编译、测试、打包等

3.1. Maven简介

Maven是一个Java项目构建工具,它可以定义项目结构、项目依赖,并使用统一的方式进行自动化构建Java应用。

通常一个Java项目需要的东西:

  • 确认依赖包
  • 确认目录结构(src放源码、resource放配置、bin放编译生成的.class字节码文件
  • 确认编译环境
  • 编写流水线编译Shell脚本,自动化构建

Maven就是确认上面这套流程的工具:

  • 提供了一套标准化的项目结构;
  • 提供了一套标准化的构建流程(编译,测试,打包,发布……);
  • 提供了一套依赖管理机制。

Maven在Mac上的安装

1
2
3
4
5
6
7
8
9
// 安装
brew install maven

// Maven环境变量配置
M2_HOME=/path/to/maven-3.6.x
PATH=$PATH:$M2_HOME/bin

// 版本
mvn -version

3.2. Maven项目结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
a-maven-project
├── pom.xml     // 项目描述文件
├── src         // 源码
│   ├── main
│   │   ├── java        // Java源码
│   │   └── resources   // 资源目录
│   └── test    // 测试代码
│       ├── java
│       └── resources
└── target      // 编译、打包生成文件存储

3.3. pom.xml配置文件

  • Maven使用pom.xml定义项目内容,并使用预设的目录结构;
  • Maven中声明一个依赖项可以自动下载并导入classpath;
  • Maven使用groupId,artifactId和version唯一确认一个依赖,Maven从中央镜像仓库下载依赖,一旦拉取就生成本地缓存(用户主目录.m2)
    • groupId:属于组织的名称;
    • artifactId:该jar包自身的名称;
    • version:该jar包的版本,以-SNAPSHOT结尾的版本被视为开发版,每次都会从Maven镜像源下载
  • 针对依赖包还有子依赖包的情况,Maven会一并将其加入到我们的项目依赖,并将其导入到我们的classpath中,不需要去管项目依赖包的子依赖包情况
  • 如果要引入一个第三方库,但不清楚该依赖库的GroupIDartifactIdversion,可以通过 search.maven.org 域名检索
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 配置文件实例
<project ...>
	<modelVersion>4.0.0</modelVersion>
    // 类似Java包名,通常组织ID
	<groupId>com.ali</groupId>
    // 类似Java类目
	<artifactId>hello</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>
	<properties>
        ...
	</properties>
    // 依赖包comons-logging情况
	<dependencies>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
	</dependencies>
</project>

包之间的依赖范围scope关系

  • compile: 编译时候需要(默认)
  • test: 编译测试时候需要
  • runtime:编译无需,但运行时刻需要
  • provided:编译需要,运行时候由JDK提供
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 测试依赖
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.3.2</version>
    <scope>test</scope>
</dependency>

// 运行时依赖
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.48</version>
    <scope>runtime</scope>
</dependency>

// 编译依赖,运行无需要依赖
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.0</version>
    <scope>provided</scope>
</dependency>

3.4. Maven通过镜像仓库下载jar依赖

申明了项目jar包依赖,并会从中央仓库(repo1.maven.org)下载依赖包(第三方库需要将jar以及其依赖上传上去),同时Maven会缓存下载的jar包到~/.m2目录下,避免每次编译过程都需要从中央仓库拉取;

仓库类型:

  • 中央仓库:repo1.maven.org,可以通过镜像仓库缓存中央仓库的模块内容,实现下依赖包载加速;
  • 私有仓库:比如公司内部使用库放在自己部署的私有仓库中,需要在~/.m2/settings.xml配置;
  • 本地仓库:通过将本地编译的class内容发布在本地,方便其他本地仓库的引用,这块不建议使用,容易导致版本不一致问题;

除了通过中央仓库下载依赖jar包,还可以通过镜像拉取,比如阿里云Maven镜像资源,可以通过在~/.m2中创建一个setting.xml,配置镜像仓库:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<settings>
    <mirrors>
        <mirror>
            <id>aliyun</id>
            <name>aliyun</name>
            <mirrorOf>central</mirrorOf>
            <!-- 国内推荐阿里云的Maven镜像 -->
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        </mirror>
    </mirrors>
</settings>

3.5. 构建项目

Maven支持标准化结构,还有一套标准化构建流程,通过一系列阶段(Phase)完成软件构建的生命周期(LifeCycle),Phase阶段包含校验、初始化、source、resources、编译、测试、构建包、集成测试、安装、部署等一些内容,每个Phase步骤,又会包含多个Goal执行具体的细节内容。

常用maven操作:

  • 编译
    • mvn compile,执行Maven默认流程,一直运行到compile步骤
  • 打包
    • mvn package: 执行Maven默认流程,一直运行到package步骤
  • 清理:
    • mvn clean:清理所有生成的class和jar
    • mvn clean compile,先清理,再执行到compile;
    • mvn clean test,先清理,再compile,最后执行test;
    • mvn clean package: 先清理,再执行到package

另外一点,Maven实际是通过plugin机制来执行Phase的,比如compiler:compile是一个编译节点的Goal,Maven实际上只是去调用这个Goal,具体怎么做还是依赖一插件实现

3.6. 多模块情况

我们可以将一个大项目拆分成若干小模块,以降低软件复杂度;假定A、B模块都有类似的依赖,可以独立抽离出一个parent pom来负责存储公共依赖(注意公共parent的pom.xml的packaging字段为pom,而非jar);在子模块pom.xml可以通过<parent>引入parent模块内容;同时根pom.xml可以通过modules包括几个模块,方便Maven统一清理编译等操作:

分多模块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 目录划分
multiple-project
├── pom.xml
├── parent
│   └── pom.xml
├── module-a
│   ├── pom.xml
│   └── src
├── module-b
│   ├── pom.xml
│   └── src
└── module-c
    ├── pom.xml
    └── src

parent/pom.xml

1
2
3
4
5
6
7
8
9
<project ...>
...
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>parent</name>
...公共依赖
</project>

module-a/pom.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<project ...>
<!-- // 声明parent部分 -->
<parent>
    <groupId>com.itranswarp.learnjava</groupId>
    <artifactId>parent</artifactId>
    <version>1.0</version>
    <relativePath>../parent/pom.xml</relativePath>
</parent>
<!-- // 模块a声明 -->
<artifactId>module-a</artifactId>
<packaging>jar</packaging>
<name>module-a</name>
...A独立依赖
</project>

根pom.xml

囊括所有pom.xml,执行mvn clean package时,一起编译4个模块内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<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/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itranswarp.learnjava</groupId>
    <artifactId>build</artifactId>
    <version>1.0</version>
    <packaging>pom</packaging>
    <name>build</name>

    <modules>
        <module>parent</module>
        <module>module-a</module>
        <module>module-b</module>
        <module>module-c</module>
    </modules>
</project>

3.7. 项目使用指定版本Maven - mvnw(Maven Wrapper)

通常所有项目使用统一全局的Maven,但某些情况某些项目依赖特定版本Maven,这类情况可以通过mvnw - Maven Wrapper解决,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 安装mvnw,通过mvn指定安装0.7.6最新版Maven-Wrapper,同时指定当前项目版本的Maven为`3.3.3`
mvn -N io.takari:maven:0.7.6:wrapper -Dmaven=3.3.3
// 可以通过-X查看错误调试信息
mvn -X io.takari:maven:0.7.6:wrapper -Dmaven=3.3.3

// 查看
my-project
├── .mvn
│   └── wrapper
│       ├── MavenWrapperDownloader.java
│       ├── maven-wrapper.jar
│       └── maven-wrapper.properties
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    └── test

安装完后,需要把生成的.mvnmvnwmvnw.cmd一并提交到仓库,方便其他同学拉取后,使用同样版本Maven拉取依赖:

1
2
// 通过mvnw拉取
./mvnw clean package

3.8. 推送模块库文件到Maven仓库

以单独静态文件发布

推送步骤

  1. 初始化Maven项目目录: pox.xml,src,target
  2. 仓库编写pom.xml,依次填入源软件包仓库URL,定义Build源码和文档插件,仓库描述,依赖情况等信息
  3. 执行mvn clean package deploy进行发布仓库
 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
// pom参考
<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>组织ID</groupId>
    <artifactId>包ID</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>15</maven.compiler.source>
        <maven.compiler.target>15</maven.compiler.target>
        <java.version>15</java.version>
    </properties>

    <!-- 仓库描述 -->
    <repositories>
        <repository>
            <id>仓库ID</id>
            <name>仓库名称</name>
            <url>{仓库地址}</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            ...
        </dependency>
    </dependencies>
</project>