# 构建

  • 简单的 Java 项目可以用 javac 命令直接编译,复杂的 Java 项目则通常用 Ant、Maven、Gradle 等构建工具自动地编译、打包。
  • 构建 Java 项目时,通常将编译生成的类文件,和配置文件、依赖 jar 等文件一起,按 ZIP 格式做成一个压缩包,扩展名为 .jar 。
    • 构建 Java Web 项目时,通常打包的扩展名为 .war 。
    • jar 或 war 包是一个独立的 Java 程序,可以被 java 命令直接运行。
  • Artifact :泛指项目的构建产物,又称为工件、构建物、制品。

# 编译

# 原理

  • Java 代码需要先编译才能运行,其流程如下:

    1. 将 .java 源文件编译成字节码,存储在 .class 类文件中。
    2. 由 JVM 读取字节码,解释成当前操作系统的机器代码并执行。
  • 类文件 :扩展名为 .class ,用于存储 .java 源文件编译生成的字节码,因此又称为字节码文件。

  • 字节码(Byte-code):一种二进制代码,是介于编程语言代码与机器代码之间的中间代码。

  • JDK :Java 开发环境工具包(Java SE Development Kit),包含 JRE 以及开发、调试、监控工具。

  • JRE :Java 运行环境工具包 (Java Runtime Environment),只提供了运行 Java 程序所需的 JVM 、类库,因此体积比 JDK 小。

  • JVM :Java 虚拟机(Java Virtual Machine),用于运行 Java 的类文件。

    • Java 语言在不同操作系统上分别实现了 JVM ,使得同一个类文件可以被不同平台的 JVM 运行,而不必考虑平台的底层差异,不必修改代码或重新编译。
    • C 代码编译是直接生成当前操作系统的机器代码,而 Java 代码编译是生成字节码,以便移植到不同机器平台的 JVM 上运行。

# 流程

基本的编译流程如下:

  1. 安装 JDK
  2. 编写一个源文件 Hello.java :
    public class Hello {
      public static void main(String[] args) {
          System.out.println("Hello world");
      }
    }
    
  3. 用 javac 命令编译源文件,生成类文件:
    [[email protected] ~]# ls
    Hello.java
    [[email protected] ~]# javac Hello.java     # 这会编译生成一个类文件 Hello.class
    [[email protected] ~]# ls
    Hello.class  Hello.java
    
  4. 用 java 命令运行类文件(实际上是交给 JVM 运行):
    [[email protected] ~]# java Hello
    Hello world
    [[email protected] ~]# java Hello.class     # 运行类文件时不需要输入扩展名 .class ,否则会被当做类名的一部分
    Error: Could not find or load main class Hello.class
    

# JDK

# 安装

  • 用包管理工具安装 JDK :

    yum install java-1.8.0-openjdk-devel
    
    apt install openjdk-8-jdk
    
  • 或者从官网 (opens new window)下载二进制版:

    wget http://download.oracle.com/otn-pub/java/jdk/8u141-b15/336fa29ff2bb4ef291e347e091f7f4a7/jdk-8u141-linux-x64.tar.gz --header "Cookie: oraclelicense=accept-securebackup-cookie"
    tar -zxvf jdk-8u141-linux-x64.tar.gz -C /usr/local/
    
    # 配置环境变量,从而能定位 java 命令
    echo 'export JAVA_HOME=/usr/local/jdk1.8.0_141' >> /etc/profile
    echo 'export PATH=$PATH:$JAVA_HOME/bin' >> /etc/profile
    source /etc/profile
    
  • 或者用 Docker 部署:

    docker run -it --rm openjdk:8-jdk java
    

# 命令

# javac

javac
      *.java                # 编译源文件
      -sourcepath <path>    # 查找源文件的路径
      -classpath <path>     # 用于查找类库的路径。该设置会覆盖环境变量 CLASSPATH
      -d .                  # 编译出的类文件的保存路径
      -encoding utf-8       # 源文件的编码格式
  • path 默认为当前目录。如果指定多个路径,在 Winodws 上用 ; 分隔,在 Linux 上用 : 分隔。

# java

java
      <class name>                  # 运行类文件(在前台运行)
      -jar xx.jar                   # 运行 jar 包
      -jar xx.war --httpPort=8080   # 运行 war 包

      -classpath <path>             # 用于查找类库(通常保存为 jar 包)的路径。该设置会覆盖环境变量 CLASSPATH
      -cp <path>                    # 等价于 -classpath 选项
      -D  <property>=<value>        # 设置一个属性的值。可以多次使用该选项
          user.timezone=GMT+08      # 设置时区

      -version                              # 显示 JDK 或 JRE 的版本信息
      -XshowSettings:properties -version    # 显示详细的版本信息
      -XX:+PrintFlagsFinal                  # 打印所有 flag 配置参数的默认值
      -verbose jar                          # 显示 jar 包的安装目录

      # 关于内存
      -Xms1G                        # -XX:InitialHeapSize ,堆内存的初始容量(并不是最小容量),默认为主机 RAM 的 1/64
      -Xmx1G                        # -XX:MaxHeapSize ,堆内存的最大容量,默认为主机 RAM 的 1/4 。建议设置得足够大,避免因为内存不足而频繁 GC
      -Xmn300M                      # 堆内存中 young 区域的大小,剩下的堆内存则属于 old 区域。-Xmn 会同时设置 -XX:NewSize 和 -XX:MaxNewSize
      -XX:NewRatio=2                # old 区域容量是 young 的多少倍,默认为 2 。与 -Xmn 相比,该参数能按比例分配内存
      -XX:SurvivorRatio=8           # eden space 容量是 survivor space 的多少倍,默认为 8 。默认有 2 个 survivor space ,因此每个的容量为 NewSize/(8+2)
      -Xss1m                        # 每个线程的堆栈大小,默认为 1M
      -XX:MaxDirectMemorySize=1G    # Direct Memory 的最大容量,达到限制则触发一次 Full GC 。默认等于 -Xmx
      -XX:MetaspaceSize=20m         # Metaspace 的初始容量,默认为 20.8M 。超过该容量之后,每次扩容都会触发 Full GC ,因此建议设置成与 MaxMetaspaceSize 相等
      -XX:MaxMetaspaceSize=100m     # Metaspace 的最大容量,默认不限制
      -XX:+HeapDumpOnOutOfMemoryError       # 当堆内存不足时,生成堆内存的快照文件,保存到磁盘
      -XX:HeapDumpPath=java_pid$PID.hprof   # 快照文件的保存路径,支持相对路径

      # 关于 G1 GC
      -XX:+UseG1GC                  # 选择 GC 算法。比如 UseSerialGC、UseParallelGC、-XX:+UseConcMarkSweepG、UseG1GC
      -XX:G1HeapRegionSize=1M       # region 大小,需要是 2 的指数。默认会根据 HeapSize 自动决定
      -XX:MaxGCPauseMillis=100      # 每次 G1 GC 预期最多 STW 停顿多少毫秒,默认不限制。该值不应该设置得过小,否则会增加 GC 次数,而且不保证低于该耗时
      -XX:GCTimeRatio=50            # 控制 GC 耗时,取值范围为 1~99 ,默认值为 99 。假设 JVM 累计运行时长为 T ,则 GC 累计耗时预期小于 T / (1+GCTimeRatio)

      # 关于 GC 日志
      -XX:+PrintGCDetails           # 打印 GC 详细日志。默认不打印
      -XX:+PrintGCDateStamps        # 每行 GC 日志的开头加上日期时间戳
      -Xloggc:gc.log                # 将 GC 日志保存到文件中。默认打印到 stdout
      -XX:+UseGCLogFileRotation     # 自动轮换日志文件
      -XX:NumberOfGCLogFiles=1      # 保留多少个日志文件,默认不限制。命名格式为 gc.log.0、gc.log.1 等
      -XX:GCLogFileSize=8M          # 每个日志文件的最大体积

      -XX:+OmitStackTraceInFastThrow  # 重复打印一个异常时,省略堆栈信息。默认启用了该标志
  • 一般命令格式为 java [-options] -jar xx.jar ,如果将 -options 放在 -jar xx.jar 之后,则不会生效。
    • 可用 -XX:+FLAG 的格式启动一个功能标志,用 -XX:-FLAG 的格式关闭标志。
  • 当 Java 进程申请内存时,如果堆内存的空闲内存不足,则会自动进行 GC 。
    • 频繁 GC 会造成较大 CPU 负载,可能导致 Java 进程卡死、端口无响应。
    • 如果 GC 之后依然内存不足,则会抛出 OutOfMemoryError 异常,可能导致 Java 进程终止。
      • 建议启用 -XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 在抛出 OutOfMemoryError 异常时自动保存堆快照文件,记录所有对象的内存大小等信息,便于排查。
      • 堆快照文件是一种二进制文件,需要用 MAT、jmap 等工具分析。
  • 建议监控 Java 进程平时实际占用的内存大小,据此设置内存参数 -Xmx 。
    • 建议设置的 -Xms 等于 -Xmx ,让 JVM 启动时就一次向操作系统申请这么多内存,因为频繁申请内存的开销较大。不过 JVM 运行时占用的堆内存依然可能低于 -Xms 。
    • JVM 运行时,即使实际占用的堆内存低于 -Xmx ,也可能触发 GC 。但只会销毁垃圾对象,不会释放空闲内存,而是留给 JVM 自己以后使用。因为释放内存、重新申请内存的开销都较大。
      • 例如 JVM 向操作系统申请了 6G 堆内存,实际只使用 2G 内存,空闲的 4G 内存也占用着不释放。
      • 采用 Serial GC 时,可以调整以下配置参数,强制让 JVM 释放空闲内存,但会增加 CPU 开销。
        -XX:MinHeapFreeRatio=0      # GC 之后,空闲内存的预期最小百分比。如果低于该值,则申请更多空闲内存
        -XX:MaxHeapFreeRatio=100    # GC 之后,空闲内存的预期最大百分比。如果超过该值,则释放一些空闲内存,还给操作系统。默认为 100% ,即不释放
        

# jps

jps       # 列出当前主机上运行的所有 JVM 进程,及其 PID
    -l    # 显示执行的 jar 包名,或主类名

# Ant

:一个 Java 项目的构建工具,可以自动进行 Java 项目的编译、测试、打包。

  • 官方文档 (opens new window)
  • Ant 最初是用于构建 Tomcat ,后来在 2000 年作为一个独立项目发布。
  • 用法:
    1. 为 Java 项目创建一个 build.xml 文件,作为 Ant 的配置文件。
    2. 使用 ant 命令编译:
      ant [target]...
          -f build.xml
      

# 配置

build.xml 的内容示例:

<?xml version="1.0" encoding="UTF-8"?>

<!-- 项目名、项目目录、默认执行的任务 -->
<project name="test" basedir="/opt/test" default="compile">

    <!-- 定义一些属性,属性名由用户自定义,可像变量一样使用 -->
    <property name="src.dir"     value="src"/>
    <property name="lib.dir"     value="lib"/>
    <property name="classes.dir" value="classes"/>  <!-- 编译出的类文件的保存路径 -->
    <property name="build.dir"   value="build"/>
    <property name="jre.dir"     value="/usr/local/jdk1.8.0_101/jre/lib"/>

    <!-- 配置一些路径 -->
    <path id="compile.dependencies">
        <fileset dir="${lib.dir}" includes="*.jar"/>
    </path>
    <path id="java.dependencies">
        <fileset dir="${jre.dir}" includes="*.jar"/>
    </path>

    <!-- 定义一个 target ,作为被 Ant 执行的任务 -->
    <target name="init">
        <delete dir="${classes.dir}"/>
        <delete dir="${build.dir}"/>
        <mkdir  dir="${classes.dir}"/>
        <mkdir  dir="${build.dir}"/>
    </target>

    <target name="compile" depends="init">
        <javac debug="true" includeantruntime="false" srcdir="${src.dir}" destdir="${classes.dir}" encoding="UTF-8">
            <!-- <compilerarg line="-XDignore.symbol.file"/> -->
            <classpath>
                <path refid="compile.dependencies"/>
                <path refid="java.dependencies"/>
            </classpath>
        </javac>
    </target>

    <target name="build_war" depends="compile">
        <war destfile="${build.dir}/${ant.project.name}.war" webxml="${build.dir}/WEB-INF/web.xml">
            <fileset dir="${build.dir}"/>
            <lib     dir="${build.dir}/WEB-INF/lib"/>
            <classes dir="${build.dir}/WEB-INF/classes"/>
        </war>
    </target>

</project>

# Maven

:一个 Java 项目的构建、管理工具,既可以构建 Java 项目,还可以下载、制作、管理 jar 包等构建产物。

  • 官方文档 (opens new window)
  • 读音为 ['meɪv(ə)n]
  • 于 2004 年发布,比 Ant 的功能更多。
  • 基于 POM(Project Object Model ,项目对象模型)管理 Java 项目。
  • 用法:
    1. 调整 Java 项目的目录结构,以符合 POM 的规范。
    2. 使用 mvn 命令编译。

# 安装

  • 用包管理工具安装:
    yum install maven
    
  • 或者下载二进制版:
    VERSION=3.8.4
    wget https://dlcdn.apache.org/maven/maven-3/$VERSION/binaries/apache-maven-$VERSION-bin.tar.gz
    tar -zxvf apache-maven-$VERSION-bin.tar.gz -C /usr/local/
    echo "export MAVEN_HOME=/usr/local/apache-maven-$VERSION" >> /etc/profile
    echo "export PATH=$PATH:$MAVEN_HOME/bin" >> /etc/profile
    source /etc/profile
    
  • 或者用 Docker 部署:
    docker run -it --rm \
        -v maven-repo:/root/.m2 \   # 挂载本地仓库
        -v $PWD:/data \             # 挂载项目目录
        --workdir /data \
        maven:3.8.4-jdk-8 \
        mvn clean package
    

# 命令

mvn
    compile           # 编译当前项目
    test              # 在 compile 的基础上,测试项目。这会先编译出测试用例的类文件,然后执行它们
    package           # 在 test    的基础上,打包项目。一般是生成 jar 包或 war 包
    install           # 在 package 的基础上,将构建产物安装到本地仓库
    deploy            # 在 install 的基础上,将构建产物上传到远程仓库

    clean             # 清理项目目录,比如删除 target 目录下的文件
    clean package     # 先执行 clean ,再执行另一个操作(也可以换成 install 等操作)

    -D  <property>=<value>    # 设置一个属性的值。可以多次使用该选项
        maven.test.skip=true  # 跳过 test 步骤
        file.encoding=UTF-8   # 设置编码格式
    -P  dev,test              # 激活一些配置文件(profiles),用逗号分隔

    -pl module1,module2,...   # --projects ,只构建指定的模块。默认构建当前项目的所有模块
    -am                       # --also-make ,指定了 -pl 时,也构建它们依赖的模块

    -v                # 显示版本信息
    -X                # 显示调试信息
    dependency:list   # 列出项目的所有依赖
    dependency:tree   # 以树形结构列出项目的所有依赖,可以看出它们之间的依赖关系
  • 执行 mvn deploy 时,会依次执行多个步骤:resources、compile、testResources、testCompile、test、jar、install、deploy 。
    • 执行 mvn package 时,只是执行到 jar 打包步骤就停止。
    • 执行 mvn clean deploy 时,会先执行 clean 步骤。
  • 执行 mvn 命令时,会创建一个 java 子进程来进行 Maven 构建。
    • 建议声明环境变量 export MAVEN_OPTS=-Xmx1g 来限制其占用的内存。默认会占用较多内存,却并不能明显提高编译速度。
  • 例:创建一个 webapp 类型的项目
    mvn archetype:generate
        -DgroupId=com.example     # 组织名(网址倒序)
        -DartifactId=web_demo     # 项目名
        -DarchetypeArtifactId=maven-archetype-webapp  # 使用的项目模板
        -DinteractiveMode=false   # 是否进入交互模式
    
  • 例:安装指定路径的 jar 包到本地仓库
    mvn install:install-file -Dfile=targets/demo.jar \
        -DgroupId=com.test -DartifactId=demo -Dversion=0.0.1 -Dpackaging=jar
    
  • 例:下载指定的 jar 包到本地仓库
    mvn org.apache.maven.plugins:maven-dependency-plugin:2.10:get \
        -Dartifact=com.test:demo:0.0.1
        # -DremoteRepositories=http://repo1.maven.org/maven2/   # 指定远程仓库,这会覆盖 pom.xml 和 settings.xml 的配置
        # -Dtransitive=true                                     # 是否下载依赖的其它包
    

# 目录结构

  • Maven 项目的目录结构示例:

    web_demo
    ├── pom.xml                 # 该 Maven 项目的配置文件
    ├── src
    │   ├── main                # 存放源代码
    │   │    ├── java           # 存放代码文件
    │   │    ├── resources      # 存放资源文件
    │   │    └── webapp
    │   └── test                # 存放测试代码
    │       ├── java
    │       └── resources
    └── target                  # 存放 Maven 的编译产物
        ├── classes
        ├── maven-archiver
        │   └── pom.properties
        ├── web_demo            # 编译后的 webapp 目录,可以打包成 war 包
        │   ├── index.jsp
        │   ├── META-INF
        │   └── WEB-INF
        │       ├── classes
        │       └── web.xml
        └── web_demo.war        # war 包
    
  • Maven 项目支持创建多个模块(module)。

    • 每个模块相当于一个子 Maven 项目,位于一个子目录中。分别有一个 pom.xml ,继承父 pom.xml 。
    • 目录结构示例:
      web_demo
      ├── pom.xml
      ├── module1
      │   ├── pom.xml
      │   └── src
      └── module2
          ├── pom.xml
          └── src
      

# pom.xml

配置文件 pom.xml 示例:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/SETTINGS/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

    <!-- 声明 pom.xml 的格式版本 -->
    <modelVersion>4.0.0</modelVersion>

    <!-- 该 Maven 项目的首要信息,用于在编译后打包成一个工件 -->
    <name>test_project</name>
    <description>The project is for test</description>
    <groupId>in.freewind</groupId>
    <artifactId>test_project</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <!-- 声明该项目的所有模块。会自动根据它们之间的依赖关系,确定编译顺序 -->
    <modules>
      <module>module1</module>
      <module>module2</module>
    </modules>

    <!-- 声明构建项目时的配置。建议采用默认配置 -->
    <build>
      <sourceDirectory> ${basedir}/src/main/java </sourceDirectory>
      <outputDirectory> ${basedir}/target/classes </outputDirectory>
      <testSourceDirectory> ${basedir}/src/test/java </testSourceDirectory>
      <testOutputDirectory> ${basedir}/target/test-classes </testOutputDirectory>
    </build>

    <!-- 声明一些属性,相当于全局变量,可以在 pom.xml 文件的其它位置用 ${propertie} 的方式调用 -->
    <properties>
      <!-- JDK 的版本 -->
      <java.version>1.8</java.version>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
      <!-- 编码格式 -->
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <!-- 声明用于下载工件的远程仓库。可以声明多个,依次尝试从其中下载工件。如果都不可用,则默认采用中央仓库 central -->
    <repositories>
      <repository>
        <id>releases</id>
        <url>http://nexus.test.com/repository/maven-releases/</url>
        <!-- 仓库默认允许下载 releases 工件,不允许下载 snapshots 工件 -->
        <!--
        <releases>
          <enabled>true</enabled>
        </releases>
        <snapshots>
          <enabled>false</enabled>
        </snapshots>
        -->
      </repository>
      <repository>
        <id>snapshots</id>
        <url>http://nexus.test.com/repository/maven-snapshots/</url>
        <snapshots>
          <enabled>true</enabled>
          <!-- 更新工件的频率,即下载相同名称、版本,但修改时间更加新的工件。默认为 daily -->
          <updatePolicy>always</updatePolicy>
        </snapshots>
      </repository>
    </repositories>

    <!-- 声明用于上传工件的远程仓库 -->
    <distributionManagement>
      <repository>
        <id>releases</id>
        <url>http://nexus.test.com/repository/maven-releases/</url>
      </repository>
      <!-- 如果声明了 snapshotRepository ,则采用它上传 snapshot 工件,否则采用 repository -->
      <snapshotRepository>
        <id>snapshots</id>
        <url>http://nexus.test.com/repository/maven-snapshots/</url>
      </snapshotRepository>
    </distributionManagement>

    <!-- 声明该项目依赖的所有工件 -->
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>1.1.5</version>
        <!-- <type>jar</type> -->
      </dependency>
      <dependency>
        <groupId>com.test</groupId>
        <artifactId>test</artifactId>
        <version>1.0.0-SNAPSHOT</version>
      </dependency>
    </dependency>

    <!-- 可以定义一些可选配置(profile),用于覆盖当前的 POM 配置。可以通过 mvn -P 选项主动激活,也可以通过 activation 条件自动激活 -->
    <profiles>
      <profile>
        <id>dev</id>
        <properties>
          <env>dev</env>
        </properties>
        <activation>
          <activeByDefault>false</activeByDefault>
          <jdk>1.8</jdk>
        </activation>
      </profile>
      <profile>
        <id>test</id>
        <repositories>...</repositories>
      </profile>
    </profiles>

</project>
  • 官方文档 (opens new window)
  • Artifact 的命名格式为 groupId:artifactId:version[:packaging[:classifier]]
    • groupId :制作者的团体名称,一般使用倒序的域名。
    • artifactId :工件的名称,在同一个 groupId 下唯一。
    • version :工件的版本。分为两种格式:
      • releases :正式版本,比如 1.0.0 。构建项目时,先尝试从本地仓库获取相同名称、版本的依赖工件,如果不存在,再从远程仓库下载到本地仓库,缓存起来。
      • snapshots :快照版本,比如 1.0.0-SNAPSHOT 。构建项目时,总是尝试从远程仓库获取相同名称、版本,但修改时间更加新的依赖工件,覆盖本地仓库的缓存。
    • packaging :打包格式,默认为 jar 。
    • classifier :分类器,可以是一个任意字符串。
  • Maven 仓库用于存储 Artifact 文件,分为两种:
    • 本地仓库
      • :位于本机某个目录下(默认为 ~/.m2/repository/),用于缓存从远程仓库下载的依赖。
    • 远程仓库
      • :位于其它主机上的仓库,可用于下载、上传工件。比如用 Nexus 搭建的私有仓库。
      • Maven 社区维护了一个远程仓库,称为中央仓库(central)。可在 https://search.maven.org/ 网页搜索,或浏览其文件列表 https://repo1.maven.org/maven2/
      • 在 pom.xml 中可以配置当前项目使用的远程仓库,在 settings.xml 中可以配置全局的远程仓库。

# settings.xml

执行 mvn 命令时,还会读取以下位置的配置文件:

~/.m2/settings.xml              # 当前用户的配置文件
$MAVEN_HOME/conf/settings.xml   # 全局的配置文件

settings.xml 示例:

<?xml version="1.0" encoding="UTF-8"?>

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

  <!-- 配置远程仓库的访问账号 -->
  <servers>
    <server>
        <id>releases</id>
        <username>***</username>
        <password>***</password>
    </server>
    <server>
        <id>mirror1</id>
        <username>***</username>
        <password>***</password>
    </server>
  </servers>

  <!-- 声明镜像仓库 -->
  <mirrors>
    <mirror>
      <id>mirror1</id>
      <!-- 用该镜像仓库替换哪些 id 的远程仓库 -->
      <mirrorOf>*</mirrorOf>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    </mirror>
  </mirrors>

</settings>
  • 镜像仓库(mirror):一些更高优先级的远程仓库。可以拦截发向普通远程仓库的请求,交给镜像仓库处理。
    • 只会拦截下载工件的请求,不会拦截上传工件的请求。
    • 如果一个远程仓库匹配多个镜像仓库,则只采用第一个镜像仓库。
    • 如果镜像仓库不可访问,并不会再尝试访问原来的远程仓库。
    • mirrorOf 的取值示例:
      *             # 匹配所有远程仓库
      central       # 匹配中央仓库
      repo1,repo2   # 匹配指定 id 的远程仓库,不支持匹配 mirror id
      !repo1,*      # 排除 repo1 ,然后匹配其它远程仓库
      

# Gradle

:一个 Java 项目的构建工具。