maven笔记

发布于 2022-04-02  671 次阅读


一、 Maven简介

Maven是专门用于管理和构建Java项目的工具,它的主要功能有:

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

1.1.1 Maven模型

  • 项目对象模型 (Project Object Model)
  • 依赖管理模型(Dependency)
  • 插件(Plugin)

1.1.2 仓库

  • 本地仓库:自己计算机上的一个目录
  • 中央仓库:由Maven团队维护的全球唯一的仓库
  • 远程仓库(私服):一般由公司团队搭建的私有仓库

1.3 Maven基本使用

1.3.1 Maven 常用命令

命令说明
mvn –version显示版本信息
mvn clean清理项目生产的临时文件,一般是模块下的target目录
mvn compile编译源代码,一般编译模块下的src/main/java目录
mvn package项目打包工具,会在模块下的target目录生成jar或war等文件
mvn test测试命令,或执行src/test/java/下junit的测试用例
mvn install将打包的jar/war文件复制到你的本地仓库中,供其他模块使用
mvn deploy将打包的文件发布到远程参考,提供其他人员进行下载依赖
mvn site生成项目相关信息的网站
mvn dependency:tree打印出项目的整个依赖树
mvn archetype:generate创建Maven的普通java项目
mvn tomcat:run在tomcat容器中运行web应用

Maven有三个内置的生命周期:默认(default),清洁(clean)和站点(site)

  • 清洁(clean) 为执行以下工作做必要的清理。就是我们经常做的,删除target文件夹。
  • 默认(default) 真正进行项目编译打包等工作的阶段
  • 站点(site) 生成项目报告,站点,发布站点

默认(default)的生命周期包括以下阶段(该阶段经过简化,实际上更加复杂):

  1. 验证(validate) - 验证项目是否正确,所有必要的信息可用。
  2. 编译(compile) - 编译项目的源代码。
  3. 测试(test) - 使用合适的单元测试框架测试编译的源代码。这些测试不应该要求代码被打包或部署。
  4. 打包(package)- 采用编译的代码,并以其可分配格式(如JAR)进行打包。
  5. 验证(verify) - 对集成测试的结果执行任何检查,以确保满足质量标准。
  6. 安装(install) - 将软件包安装到本地存储库中,用作本地其他项目的依赖项。
  7. 部署(deploy) - 在构建环境中完成,将最终的包复制到远程存储库以与其他开发人员和项目共享(私服)。
mvn install

此命令在执行安装之前按顺序(验证(validate),编译(compile),打包(package)等)执行每个默认生命周期阶段。在这种情况下,您只需要调用最后一个构建阶段来执行,安装(install)。

在构建环境中,使用以下调用将工件清理地构建并部署到共享存储库中。

mvn clean deploy

相同的命令可以在多模块场景(即具有一个或多个子项目的项目)中使用。Maven遍历每个子项目并执行清洁(clean),然后执行部署(deploy)(包括所有之前的构建阶段步骤)。

注意:在我们开发阶段,有一些生命周期的阶段,比如验证(validate)这些,基本很少用到。只要使用关键的几个基本能满足需求。

1.4 依赖管理

1.4.1 使用坐标引入jar包

使用坐标引入jar包的步骤:

  • 在项目的 pom.xml 中编写 <dependencies> 标签
  • 在 <dependencies> 标签中 使用 <dependency> 引入坐标
  • 定义坐标的 groupId,artifactId,version

1.4.2 依赖范围

通过设置坐标的依赖范围(scope),可以设置 对应jar包的作用范围:编译环境、测试环境、运行环境。

junit 依赖通过 scope 标签指定依赖的作用范围。 那么这个依赖就只能作用在测试环境,其他环境下不能使用。

那么 scope 都可以有哪些取值呢?

依赖范围编译classpath测试classpath运行classpath例子
compileYYYlogback
test-Y-Junit
providedYY-servlet-api
runtime-YYjdbc驱动
systemYY-存储在本地的jar包
  • compile :作用于编译环境、测试环境、运行环境。
  • test : 作用于测试环境。典型的就是Junit坐标,以后使用Junit时,都会将scope指定为该值
  • provided :作用于编译环境、测试环境。我们后面会学习 servlet-api ,在使用它时,必须将 scope 设置为该值,不然运行时就会报错
  • runtime : 作用于测试环境、运行环境。jdbc驱动一般将 scope 设置为该值,当然不设置也没有任何问题

注意:

  • 如果引入坐标不指定 scope 标签时,默认就是 compile 值。以后大部分jar包都是使用默认值。

maven标准目录

src                                 
 |--main
     |--java         源代码目录
     |--resources    资源目录      
 |--test
     |--java         测试代码目录
     |--resources    测试资源目录
|--target
 |--classes      编译后的class文件目录
 |--test-classes 编译后的测试class文件目录
pom.xml             Maven工程配置文件

二、Maven依赖(重点)

maven管理依赖也就是jar包牛逼之处是不用我们自己下载,会从一些地方自动下载

maven工程中我们依靠在pom.xml文件进行配置完成jar包管理工作(依赖)

在工程中引入某个jar包,只需要在pom.xml中引入jar包的坐标,比如引入log4j的依赖:

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

Maven 通过 groupId、 artifactId 与 version 三个向量来定位Maven仓库其jar包所在的位置,并把对应的jar包引入到工程中来。

jar包会自动下载,流程如下

image-20210119141859117

2.1、依赖范围

了解、classpath是个什么东西

顾明思议,就是编译好的class文件所在的路径。

事实上,我们的类加载器(classloader)就是去对应的classpath中加在class二进制文件。

普通java项目

image-20210119142123678

META-INF中有个文件,有以下内容,告诉jvm执行的时候去哪个类里找main方法。

普通的java工程类路径就是最外层的目录。

Manifest-Version: 1.0
Main-Class: com.xinzhi.HelloUser

web项目

咱们的src打包后会放在

image-20210119142224760

src目录下的配置文件会和class文件一样,自动copy到应用的 WEB-INF/classes目录下 ,所以普通jar包的类路径就是根路径,没有资源,如果有配置文件也放在src目录下,他会同步打包在类路径下。

所以web项目的classpath是 WEB-INF/classes

maven项目

maven工程会将src/main/java 和 src/main/resources 文件夹下的文件全部打包在classpath中。运行时他们两个的文件夹下的文件会被放在一个文件夹下。

maven 项目不同的阶段引入到classpath中的依赖是不同的,例如,

  • 编译时,maven 会将与编译相关的依赖引入classpath中
  • 测试时,maven会将测试相关的的依赖引入到classpath中
  • 运行时,maven会将与运行相关的依赖引入classpath中

而依赖范围就是用来控制依赖于这三种classpath的关系。

scope标签就是依赖范围的配置

该项默认配置compile,可选配置还有test、provided、runtime、system、import。

其中compile、test和provided使用较多,下面依次介绍。

有些jar包(如selvlet-api)运行时其实是不需要的,因为tomcat里有,但编译时是需要的,因为编译的时候没有tomcat环境

有些jar只在测试的时候才能用到。比如junit,真是运行不需要的

有些jar运行,测试时必须要有,编译时不需要,如jdbc驱动,编译时用的都是jdk中的接口,运行时我们才使用反射注册了驱动。

向以上的这些jar包不是说使用默认的compile一定不行,但是设置成合适的范围更好,当然有事会有问题,比如你引入的servlet-api和tomcat自带的不一样,就会出问题。

1.1 编译依赖范围(compile)

该范围就是默认依赖范围,此依赖范围对于编译、测试、运行三种classpath都有效,举个简单的例子,假如项目中有fastjson的依赖,那么fastjson不管是在编译,测试,还是运行都会被用到,因此fastjson必须是编译范围(构件默认的是编译范围,所以依赖范围是编译范围的无须显示指定)

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.68</version>
</dependency>

1.2 测试依赖范围(test)

使用此依赖范围的依赖,只对测试classpath有效,在编译主代码和项目运行时,都将无法使用该依赖,最典型的例子就是 Junit, 构件在测试时才需要,所以它的依赖范围是测试,因此它的依赖范围需要显示指定为、、<scope>test</scope>,当然不显示指定依赖范围也不会报错,但是该依赖会被加入到编译和运行的classpath中,造成不必要的浪费 。

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.7</version>
    <scope>test</scope>
</dependency>

1.3 已提供依赖范围(provided)

使用该依赖范围的maven依赖,只对编译和测试的classpath有效,对运行的classpath无效,典型的例子就是servlet-api, 编译和测试该项目的时候需要该依赖,但是在运行时,web容器已经提供的该依赖,所以运行时就不再需要此依赖,如果不显示指定该依赖范围,并且容器依赖的版本和maven依赖的版本不一致的话,可能会引起版本冲突,造成不良影响。

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

1.4 运行时依赖范围(runtime)

使用该依赖范围的maven依赖,只对测试和运行的classpath有效,对编译的classpath无效,典型例子就是JDBC的驱动实现,项目主代码编译的时候只需要JDK提供的JDBC接口,只有在测试和运行的时候才需要实现上述接口的具体JDBC驱动。

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.25</version>
    <scope>runtime</scope>
</dependency> 

2、依赖的传递

jar其实也是别人写的工程,他也会依赖其他的jar包,传递性让我们可以不用关系我们所依赖的jar他依赖了哪些jar,只要我们添加了依赖,他会自动将他所依赖的jar统统依赖进来。

image-20210119142319854

我们只需依赖A.jar,其他的会自动传递进来。

依赖传递的原则:

  • 最短路径优先原则:如果A依赖于B,B依赖于C,在B和C 中同时有log4j的依赖,并且这两个版本不一致,那么A会根据最短路径原则,在A中会传递过来B的log4j版本。
image-20210119142342227
  • 路径相同先声明原则:如果我们的工程同时依赖于B和A,B和C没有依赖关系,并且都有D的依赖,且版本不一致,那么会引入在pom.xml中先声明依赖的log4j版本。
image-20210119142458755
<dependency>
    <groupId>com.xinzi</groupId>
    <artifactId>B</artifactId>
    <version>1.5.3</version>
</dependency> 
<dependency>
    <groupId>com.xinzhi</groupId>
    <artifactId>A</artifactId>
    <version>1.12.2</version>
</dependency> 

因为1.2.3先声明,所以获胜。

特别注意:

不同版本的jar选一个会导致一个问题,1.3.2版本高,A.jar可能用到了高版本的一些新的方法,此时因为某些原因系统选择了低版本,就会导致A.jar报错,无法运行。那么就要想办法把低版本排除掉,一般高版本会兼容低版本。

2.3、依赖的排除

结合上个例子,我们想把低版本的D.jar排除了,就可以这样做,这样系统就只能依赖高版本

<dependencies>
    <dependency>
        <groupId>com.xinzi</groupId>
        <artifactId>B</artifactId>
        <version>1.5.3</version>
        <exclusions>
            <exclusion>
                <artifactId>com.xinzhi</artifactId>
                <groupId>D</groupId>
            </exlcusion>
        </exclusions>
    </dependency> 
    <dependency>
        <groupId>com.xinzhi</groupId>
        <artifactId>A</artifactId>
        <version>1.12.2</version>
    </dependency> 
</dependencies>

2.4、聚合和继承

分布式开发必须要用

聚合模块(父模块)的打包方式必须为pom,否则无法完成构建

在聚合多个项目时,如果这些被聚合的项目中需要引入相同的Jar,那么可以将这些Jar写入父pom中,各个子项目继承该pom即可。,父模块的打包方式必须为pom,否则无法构建项目。

通过在各个子模块中配置来表明其继承与哪一个父模块:

<parent>
    <artifactId>parent</artifactId>
    <groupId>org.example</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>childern-two</artifactId>

可以被继承的POM元素如下:

  • groupId:项目组ID,项目坐标的核心元素
  • version:项目版本,项目坐标的核心因素
  • properties:自定义的Maven属性 一般用于同一制定各个依赖的版本号
  • dependencies:项目的依赖配置 公共的依赖
  • dependencyManagement:项目的依赖管理配置
  • repositories:项目的仓库配置
  • build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等

一些对项目的描述

  • description:项目的描述信息
  • organization:项目的组织信息
  • inceptionYear:项目的创始年份
  • url:项目的URL地址
  • developers:项目的开发者信息
  • contributors:项目的贡献者信息
  • distributionManagement:项目的部署配置
  • issueManagement:项目的缺陷跟踪系统信息
  • ciManagement:项目的持续集成系统信息
  • scm:项目的版本控制系统
  • malilingLists:项目的邮件列表信息
  • reporting:包括项目的报告输出目录配置、报告插件配置等

三、POM文件

3.1、基础配置

一个典型的pom.xml文件配置如下:

<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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <!-- 模型版本。必须是这样写,现在是maven唯一支持的版本 -->
    <modelVersion>4.0.0</modelVersion>
    <!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.xinzhi,maven会将该项目打成的jar包放本地路径:/com/xinzhi/ -->
    <groupId>com.xinzhi</groupId>
    <!-- 本项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
    <artifactId>test</artifactId>
    <!-- 本项目目前所处的版本号 -->
    <version>1.0.0-SNAPSHOT</version>
    
    <!-- 打包的机制,如pom,jar,  war,默认为jar -->
    <packaging>jar</packaging>

    <!-- 为pom定义一些常量,在pom中的其它地方可以直接引用 使用方式 如下 :${file.encoding} -->
    <!-- 常常用来整体控制一些依赖的版本号 -->
    <properties>
        <file.encoding>UTF-8</file.encoding>
        <java.source.version>1.8</java.source.version>
        <java.target.version>1.8</java.target.version>
    </properties>
    

    <!-- 定义本项目的依赖关系,就是依赖的jar包 -->
    <dependencies>
        <!-- 每个dependency都对应这一个jar包 -->
        <dependency>
            <!--一般情况下,maven是通过groupId、artifactId、version这三个元素值(俗称坐标)来检索该构件, 然后引入你的工程。如果别人想引用你现在开发的这个项目(前提是已开发完毕并发布到了远程仓库),--> 
            <!--就需要在他的pom文件中新建一个dependency节点,将本项目的groupId、artifactId、version写入, maven就会把你上传的jar包下载到他的本地 -->
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>

            <!-- 依赖范围 -->
            <scope>complie</scope>
            <!-- 设置  依赖是否可选,默认为false,即子项目默认都继承。如果为true,
                 则子项目必需显示的引入  -->
            <optional>false</optional>
            
            <!-- 依赖排除-->
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    ...
</project>

一般来说,上面的几个配置项对任何项目都是必不可少的,定义了项目的基本属性。

除了dependencies我们还用到了dependencyManagement,区别如下

dependencies

  • 即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)。
  • 继承下来就会被编译,如果子项目根本不用这个依赖会增加子工程的负担。

dependencyManagement:通常会在父工程中定义,目的是统一各个子模块的依赖版本,有不用实际依赖

  • 只是声明依赖,并不实现引入
  • 子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;
  • 只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。

3.2、构建配置

<build>

    <!-- 产生的构件的文件名,默认值是${artifactId}-${version}。 -->
    <finalNasourceDirectory>${basedir}\src\main\java</sourceDirectory>

    <!--项目单元测试使用的源码目录,当测试项目的时候,构建系统会编译目录里的源码。该路径是相对于pom.xml的相对路径。 -->
    
    <>${basedir}\target\classes</outputDirectory>

    <!--被编译过的测试class文件存放的目录。 -->
    <testOutputDirectory>${basedir}\target\test-classes
    </testOutputDirectory>
    <!-- 以上配置都有默认值,就是约定好了目录就这么建 -->
    
    <resources>
         <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>

    <!--单元测试相关的所有资源路径,配制方法与resources类似 -->
    <testResources>
        <testResource>
            <targetPath />
            <filtering />
            <directory />
            <includes />
            <excludes />
        </testResource>
    </testResources>

    <!--使用的插件列表 。 -->
    <plugins>
        <plugin>
		...具体在插件使用中了解
        </plugin>
    </plugins>

    <!--主要定义插件的共同元素、扩展元素集合,类似于dependencyManagement, -->
    <!--所有继承于此项目的子项目都能使用。该插件配置项直到被引用时才会被解析或绑定到生命周期。 -->
    <!--给定插件的任何本地配置都会覆盖这里的配置 -->
    <pluginManagement>
        <plugins>...</plugins>
    </pluginManagement>

</build>

我们常用的几个配置

关于资源处理的配置

有些小伙伴就喜欢在src中填写配置文件

<!-- 处理资源被过滤问题 -->
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

添加本地jar包

本地jar,如:支付宝jar包放到 src/main/webapp/WEB-INF/lib 文件夹下,如果没有配置,本地没问题,但是线上会找不到sdk类,为什么要引入,因为支付宝jar包再中央仓库没有

<!-- geelynote maven的核心插件之-complier插件默认只支持编译Java 1.4,因此需要加上支持高版本jre的配置,在pom.xml里面加上 增加编译插 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
        <compilerArguments>
            <!-- 本地jar,支付宝jar包放到  src/main/webapp/WEB-INF/lib 文件夹下,
           如果没有配置,本地没问题,但是线上会找不到sdk类
           为什么要引入,因为支付宝jar包再中央仓库没有,再比如oracle连接驱动的jar
        -->
            <extdirs>${project.basedir}/src/main/webapp/WEB-INF/lib</extdirs>
        </compilerArguments>
    </configuration>
</plugin>

3.3、仓库配置

<repositories>
    <repository>
        <id>alimaven</id>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

pom.xml里面的仓库与setting.xml里的仓库功能是一样的。主要的区别在于,pom里的仓库是个性化的。比如一家大公司里的setting文件是公用的,所有项目都用一个setting文件,但各个子项目却会引用不同的第三方库,所以就需要在pom.xml里设置自己需要的仓库地址。

3.4、多环境配置

<profiles>
        <profile>
            <id>dev</id>
            <build>
                <finalName>dev</finalName>
            </build>
            <repositories>
                <repository>
                    <id>ali</id>
                    <name>ali repo</name>
                    <url>https://maven.aliyun.com/repository/central</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
        </profile>
        <profile>
            <id>test</id>
            <build>
                <finalName>test</finalName>
            </build>
            <repositories>
                <repository>
                    <id>ali</id>
                    <name>ali repo</name>
                    <url>https://mirrors.huaweicloud.com/repository/maven/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
        </profile>
        <profile>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <id>pro</id>
            <build>
                <finalName>pro</finalName>
            </build>
        </profile>
    </profiles>

项目信息配置(知道)

<!--项目的名称, Maven产生的文档用 -->
<name>banseon-maven </name>

<!--项目主页的URL, Maven产生的文档用 -->
<url>http://www.clf.com/ </url>

<!--项目的详细描述, Maven 产生的文档用。 当这个元素能够用HTML格式描述时 -->
<!--(例如,CDATA中的文本会被解析器忽略,就可以包含HTML标签),不鼓励使用纯文本描述。 -->
<!-- 如果你需要修改产生的web站点的索引页面,你应该修改你自己的索引页文件,而不是调整这里的文档。 -->
<description>A maven project to study maven. </description>


<!--项目创建年份,4位数字。当产生版权信息时需要使用这个值。 -->
<inceptionYear />

<!--项目开发者列表 -->
<developers>

    <!--某个项目开发者的信息 -->
    <developer>
        <!--SCM里项目开发者的唯一标识符 -->
        <id> HELLO WORLD </id>
        <!--项目开发者的全名 -->
        <name> banseon </name>
        <!--项目开发者的email -->
        <email> banseon@126.com</email>
        <!--项目开发者的主页的URL -->
        <url />
        <!--项目开发者在项目中扮演的角色,角色元素描述了各种角色 -->
        <roles>
            <role> Project Manager</role>
            <role>Architect </role>
        </roles>
        <!--项目开发者所属组织 -->
        <organization> demo</organization>
        <!--项目开发者所属组织的URL -->
        <organizationUrl>http://hi.clf.com/ </organizationUrl>
        <!--项目开发者属性,如即时消息如何处理等 -->
        <properties>
            <dept> No </dept>
        </properties>
        <!--项目开发者所在时区, -11到12范围内的整数。 -->
        <timezone> -5</timezone>
    </developer>

</developers>

<!--项目的其他贡献者列表 -->
<contributors>

    <!--项目的其他贡献者。参见developers/developer元素 -->
    <contributor>
        <name />
        <email />
        <url />
        <organization />
        <organizationUrl />
        <roles />
        <timezone />
        <properties />
    </contributor>

</contributors>

<!--该元素描述了项目所有License列表。应该只列出该项目的license列表,不要列出依赖项目的license列表。 -->
<!--如果列出多个license,用户可以选择它们中的一个而不是接受所有license。 -->
<licenses>

    <!--描述了项目的license,用于生成项目的web站点的license页面,其他一些报表和validation也会用到该元素。 -->
    <license>

        <!--license用于法律上的名称 -->
        <name> Apache 2 </name>
        <!--官方的license正文页面的URL -->
        <url>http://www.clf.com/LICENSE-2.0.txt </url>
        <!--项目分发的主要方式: repo,可以从Maven库下载 manual, 用户必须手动下载和安装依赖 -->
        <distribution> repo</distribution>
        <!--关于license的补充信息 -->
        <comments> Abusiness-friendly OSS license </comments>
    </license>

</licenses>