您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

Maven进阶学习指南

时间:2023-06-25 12:40:55  来源:今日头条  作者:京东云开发者

前言

当我们在开发项目时,有时需要用到外部依赖组件,例如当我们需要Json序列化的时候需要用到FastJson组件,我们可以通过下载对应jar包加载到项目中。但当一个大的项目同时需要依赖各种各样的外部服务,就存在着配置繁琐、依赖冲突等问题,因此可以通过maven来完成对应的依赖管理功能。

一、Settings配置

settings.xml用来配置maven项目中的各种参数文件,包括本地仓库、远程仓库、私服、认证等信息。

1.1 配置概述

1.1.1 全局settings、用户setting、pom的区别

  • 全局 settings.xml 是 maven 的全局配置文件,一般位于${maven.home}/conf/settings.xml,即maven文件夹下的conf中。
  • 用户 setting是maven的用户配置文件,一般位于${user.home}/.m2/settings.xml,即每位用户都有一份配置文件。
  • pom.xml 文件是项目配置文件,一般位于项目根目录下或子目录下。

配置优先级从高到低:pom.xml > 本地 settings > 全局 settings

如果这些文件同时存在,在应用配置时,会合并它们的内容,如果有重复的配置,优先级高的配置会覆盖优先级低的。

1.1.2 仓库【重要】

如前言所述,我们依赖的外部服务是需要有地方进行存储的,而存储的地方就称之为仓库。其中仓库又分为本地仓库、中央仓库、镜像仓库、私服。

其对应关系为:

 

(1)本地仓库

当项目在本地编译或运行时,直接加载本地的依赖服务无疑是最快的。默认情况下,不管在Window还是linux下,每个用户在自己用户目录下都有一个路径名为.m2/repository/的仓库目录。

而原始的本地仓库是为空的,因此maven需要知道一个网络上的仓库,在本地仓库不存在时前往下载网络上的仓库,也就是远程仓库。

(2)中央仓库

当maven未配置时,会默认请求maven的中央仓库,中央仓库包含了这个世界上绝大多数流行的开源JAVA构件,以及源码、作者信息、SCM,信息、许可证信息等,每个月这里都会接受全世界java程序员大概1亿次的访问,它对全世界java开发者的贡献由此可见一斑。

但是由于最常见的例如网络原因等,国外的中央仓库使用起来并不顺利,因此就有了下载地址为国内的中央仓库,也就是镜像仓库。

(3)镜像仓库

最常用的镜像仓库无疑是阿里云的镜像仓库(如何配置阿里云的镜像仓库后面会有讲出)。总结来说,镜像仓库就是将国外的中心仓库复制一份到国内,这样一来下载速度以及访问速度都将很快。

(4)私服

一般来说中央仓库或者镜像仓库都能满足我们的需求,但是当我们在公司内合作开发代码时,可能因为系统保密性原因,有一些其他同事开发的外部依赖只希望能够被本公司的人使用,而如果上传到镜像仓库则保密性就不复存在了。因此私服最主要的功能时存储一些公司内部不希望被公开的依赖服务。

1.2 settings配置详解

settings.xml配置了本地全局maven的相关配置。

以下是一份seetings.xml的文件配置顶级元素。

xml复制代码<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
                          https://maven.apache.org/xsd/settings-1.0.0.xsd">
      <localRepository>
      <interactiveMode>
      <usePluginRegistry>
      <offline>
      <pluginGroups>
      <servers>
      <mirrors>
      <proxies>
      <profiles>
      <activeProfiles>
</settings>

1.2.1 localRepository

用来标识本地仓库的位置

JavaScript复制代码D:/Maven/Repository

1.2.2 interactiveMode

maven 是否需要和用户交互以获得输入。默认为true。

arduino复制代码true

1.2.3 usePluginRegistry

maven 是否需要使用 plugin-registry.xml 文件来管理插件版本。

arduino复制代码false

1.2.4 offline

用来标识是否以离线模式运营maven。

当系统不能联网时,可以通过次配置来离线运行。

arduino复制代码false

1.2.5 servers

当使用maven私服时,某些私服需要配置认证信息,需要在此处填写相应的配置。之所以不写在pom.xml中是因为一般项目在上传至代码仓库时同样会将pom.xml上传,而setting.xml一般位于用户本地,因此相对比较安全。

xml复制代码<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
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <!--配置服务端的一些设置。一些设置如安全证书不应该和pom.xml一起分发。这种类型的信息应该存在于构建服务器上的settings.xml文件中。 -->
  <servers>
    <!--服务器元素包含配置服务器时需要的信息 -->
    <server>
      <!--这是server的id(注意不是用户登陆的id),该id与distributionManagement中repository元素的id相匹配。 -->
      <id>server001</id>
      <!--鉴权用户名。鉴权用户名和鉴权密码表示服务器认证所需要的登录名和密码。 -->
      <username>my_login</username>
      <!--鉴权密码 。鉴权用户名和鉴权密码表示服务器认证所需要的登录名和密码。密码加密功能已被添加到2.1.0 +。详情请访问密码加密页面 -->
      <password>my_password</password>
      <!--鉴权时使用的私钥位置。和前两个元素类似,私钥位置和私钥密码指定了一个私钥的路径(默认是${user.home}/.ssh/id_dsa)以及如果需要的话,一个密语。将来passphrase和password元素可能会被提取到外部,但目前它们必须在settings.xml文件以纯文本的形式声明。 -->
      <privateKey>${usr.home}/.ssh/id_dsa</privateKey>
      <!--鉴权时使用的私钥密码。 -->
      <passphrase>some_passphrase</passphrase>
      <!--文件被创建时的权限。如果在部署的时候会创建一个仓库文件或者目录,这时候就可以使用权限(permission)。这两个元素合法的值是一个三位数字,其对应了unix文件系统的权限,如664,或者775。 -->
      <filePermissions>664</filePermissions>
      <!--目录被创建时的权限。 -->
      <directoryPermissions>775</directoryPermissions>
    </server>
  </servers>
  ...
</settings>

1.2.6 mirrors【重要】

用来配置相应的镜像仓库。

如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。用过Maven的都知道,国外的中央仓库因为网络原因用起来太慢了,所以选择一个国内的镜像就很有必要,我推荐国内的阿里云镜像。 阿里云镜像:配置很简单,修改conf文件夹下的settings.xml文件,添加如下镜像配置:

xml复制代码<mirrors>
    <mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>        
    </mirror> 
    <mirror>
      <id>alimaven1</id>
      <name>aliyun maven1</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>*</mirrorOf>        
    </mirror>
</mirrors>

其中id与name用来标识唯一的仓库,url为镜像仓库地址,mirrorOf用来匹配当请求什么仓库依赖时使用该镜像。

这里介绍下<mirrorOf>配置的各种选项

  • <mirrorOf>*<mirrorOf>:匹配所有远程仓库。
  • <mirrorOf>external:*<mirrorOf>:匹配所有远程仓库,使用localhost的除外,使用file://协议的除外。也就是说,匹配所有不在本机上的远程仓库。
  • <mirrorOf>repo1,repo2<mirrorOf>:匹配仓库repo1h和repo2,使用逗号分隔多个远程仓库。
  • <mirrorOf>*,!repo1<mirrorOf>:匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中排除。

需要注意的是,由于镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或者停止服务的时候,Maven仍将无法访问被镜像仓库,因而将无法下载构件。

此外, maven 读取mirror 配置是 从上往下读取的,因此谨慎配置<mirrorOf>*<mirrorOf>,因为如果第一个镜像仓库配置了如此标志,那么如果该仓库即使不存在对应依赖也不会向下游查询。

1.2.7 proxies

用来配置代理。

xml复制代码<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
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <proxies>
    <!--代理元素包含配置代理时需要的信息 -->
    <proxy>
      <!--代理的唯一定义符,用来区分不同的代理元素。 -->
      <id>myproxy</id>
      <!--该代理是否是激活的那个。true则激活代理。当我们声明了一组代理,而某个时候只需要激活一个代理的时候,该元素就可以派上用处。 -->
      <active>true</active>
      <!--代理的协议。 协议://主机名:端口,分隔成离散的元素以方便配置。 -->
      <protocol>http</protocol>
      <!--代理的主机名。协议://主机名:端口,分隔成离散的元素以方便配置。 -->
      <host>proxy.somewhere.com</host>
      <!--代理的端口。协议://主机名:端口,分隔成离散的元素以方便配置。 -->
      <port>8080</port>
      <!--代理的用户名,用户名和密码表示代理服务器认证的登录名和密码。 -->
      <username>proxyuser</username>
      <!--代理的密码,用户名和密码表示代理服务器认证的登录名和密码。 -->
      <password>somepassword</password>
      <!--不该被代理的主机名列表。该列表的分隔符由代理服务器指定;例子中使用了竖线分隔符,使用逗号分隔也很常见。 -->
      <nonProxyHosts>*.google.com|ibiblio.org</nonProxyHosts>
    </proxy>
  </proxies>
  ...
</settings>

1.2.8 profiles【重要】

根据环境参数来调整构建配置的列表。用于定义一组profile

seetings中的profile是 pom.xml 中 profile 元素的裁剪版本

它包含了id、activation、repositories、pluginRepositories 和 properties 元素。这里的 profile 元素只包含这五个子元素是因为这里只关心构建系统这个整体(这正是 settings.xml 文件的角色定位),而非单独的项目对象模型设置。如果一个 settings.xml 中的 profile 被激活,它的值会覆盖任何其它定义在 pom.xml 中带有相同 id 的 profile。

(1)repositories

定义了一组远程仓库的列表,当该属性对应的profile被激活时,会使用该远程仓库。

xml复制代码<repositories>
  <!--包含需要连接到远程仓库的信息 -->
  <repository>
    <!--远程仓库唯一标识 -->
    <id>codehausSnapshots</id>
    <!--远程仓库名称 -->
    <name>Codehaus Snapshots</name>
    <!--如何处理远程仓库里发布版本的下载 -->
    <releases>
      <!--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 -->
      <enabled>false</enabled>
      <!--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。这里的选项是:always(一直),dAIly(默认,每日),interval:X(这里X是以分钟为单位的时间间隔),或者never(从不)。 -->
      <updatePolicy>always</updatePolicy>
      <!--当Maven验证构件校验文件失败时该怎么做-ignore(忽略),fail(失败),或者warn(警告)。 -->
      <checksumPolicy>warn</checksumPolicy>
    </releases>
    <!--如何处理远程仓库里快照版本的下载。有了releases和snapshots这两组配置,POM就可以在每个单独的仓库中,为每种类型的构件采取不同的策略。例如,可能有人会决定只为开发目的开启对快照版本下载的支持。参见repositories/repository/releases元素 -->
    <snapshots>
      <enabled />
      <updatePolicy />
      <checksumPolicy />
    </snapshots>
    <!--远程仓库URL,按protocol://hostname/path形式 -->
    <url>http://snapshots.maven.codehaus.org/maven2</url>
    <!--用于定位和排序构件的仓库布局类型-可以是default(默认)或者legacy(遗留)。Maven 2为其仓库提供了一个默认的布局;然而,Maven 1.x有一种不同的布局。我们可以使用该元素指定布局是default(默认)还是legacy(遗留)。 -->
    <layout>default</layout>
  </repository>
</repositories>

(2)properties

定义了一组拓展属性,当对应的profile被激活时该属性有效。

bash复制代码<!--
  1. env.X: 在一个变量前加上"env."的前缀,会返回一个shell环境变量。例如,"env.PATH"指代了$path环境变量(在windows上是%PATH%)。
  2. project.x:指代了POM中对应的元素值。例如: <project><version>1.0</version></project>通过${project.version}获得version的值。
  3. settings.x: 指代了settings.xml中对应元素的值。例如:<settings><offline>false</offline></settings>通过 ${settings.offline}获得offline的值。
  4. java System Properties: 所有可通过java.lang.System.getProperties()访问的属性都能在POM中使用该形式访问,例如 ${java.home}。
  5. x: 在<properties/>元素中,或者外部文件中设置,以${someVar}的形式使用。
 -->
<properties>
  <user.install>${user.home}/our-project</user.install>
</properties>

(3)id

全局唯一标识,如果一个 settings.xml 中的 profile 被激活,它的值会覆盖任何其它定义在 pom.xml 中带有相同 id 的 profile。

(4)pluginRepositories

同repositories差不多,不过该标签定义的是插件的远程仓库。

(5)activation

触发激活该profile的条件。

xml复制代码<activation>
  <!--profile默认是否激活的标识 -->
  <activeByDefault>false</activeByDefault>
  <!--当匹配的jdk被检测到,profile被激活。例如,1.4激活JDK1.4,1.4.0_2,而!1.4激活所有版本不是以1.4开头的JDK。 -->
  <jdk>1.5</jdk>
  <!--当匹配的操作系统属性被检测到,profile被激活。os元素可以定义一些操作系统相关的属性。 -->
  <os>
    <!--激活profile的操作系统的名字 -->
    <name>Windows XP</name>
    <!--激活profile的操作系统所属家族(如 'windows') -->
    <family>Windows</family>
    <!--激活profile的操作系统体系结构 -->
    <arch>x86</arch>
    <!--激活profile的操作系统版本 -->
    <version>5.1.2600</version>
  </os>
  <!--如果Maven检测到某一个属性(其值可以在POM中通过${name}引用),其拥有对应的name = 值,Profile就会被激活。如果值字段是空的,那么存在属性名称字段就会激活profile,否则按区分大小写方式匹配属性值字段 -->
  <property>
    <!--激活profile的属性的名称 -->
    <name>mavenVersion</name>
    <!--激活profile的属性的值 -->
    <value>2.0.3</value>
  </property>
  <!--提供一个文件名,通过检测该文件的存在或不存在来激活profile。missing检查文件是否存在,如果不存在则激活profile。另一方面,exists则会检查文件是否存在,如果存在则激活profile。 -->
  <file>
    <!--如果指定的文件存在,则激活profile。 -->
    <exists>${basedir}/file2.properties</exists>
    <!--如果指定的文件不存在,则激活profile。 -->
    <missing>${basedir}/file1.properties</missing>
  </file>
</activation>

1.2.9 ActiveProfiles

在运行时手工激活的profile。

该元素包含了一组 activeProfile 元素,每个 activeProfile 都含有一个 profile id。任何在 activeProfile 中定义的 profile id,不论环境设置如何,其对应的 profile 都会被激活。如果没有匹配的 profile,则什么都不会发生。

xml复制代码<activeProfiles>
    <!-- 要激活的profile id -->
    <activeProfile>env-test</activeProfile>
</activeProfiles>

1.2.10 激活profile的三种方式【重要】

上面有提到了两种激活的profile的方式,还有一种可以通过命令行激活profile。

(1)通过ActiveProfiles激活

如题1.2.9 可以同时激活多个profile。

(2)通过activation结果

如题1.2.8 (5)

(3)通过命令行的方式激活

也是我们经常使用的方式,例如:

css复制代码mvn -P 

我们可以通过在pom.xml或setting.xml中指定不同环境的profile,在编译构建不同的项目时,通过上述的命令行方式激活对应的profIle。例如在开发环境下:

css复制代码mvn package -P dev 

可以打包开环环境下的项目。

1.3 Q&A

1.3.1 mirrors与repositories的关系【重要】

从上文可以看到,repository标签与mirror标签都定义了一个远程仓库的位置,那么当一个依赖同时存在于两个仓库时,会先加载那个依赖呢?

这里需要阐述一下maven加载真正起作用的repository的步骤,

  1. 首先获取pom.xml中repository的集合,然后获取setting.xml中mirror中元素。
  2. 如果repository的id和mirror的mirrorOf的值相同,则该mirror替代该repository。
  3. 如果该repository找不到对应的mirror,则使用其本身。
  4. 依此可以得到最终起作用的repository集合。可以理解mirror是复写了对应id的repository。

mirror相当于一个拦截器,会拦截被mirrorOf匹配到的repository,匹配原则参照 1.2.6 ,在匹配到后,会用mirror里定义的url替换到repository。

没有配置mirror

 

配置了mirror

 

总结来说,当

二、Pom.xml详解

上章中setting.xml定义了某个环境下全局项目的相关依赖配置,而pom.xml则具体定义了某一个项目中的依赖配置。

2.1 pom元素

2.1.1 基本信息

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.0
    http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
    <!-- 模型版本 一般不用更改 -->
    <modelVersion>4.0.0</modelVersion>
    <!-- 公司或者组织的唯一标志,也是打包成jar包路径的依据 -->
    <!-- 例如com.companyname.project-group,maven打包jar包的路径:/com/companyname/project-group -->
    <groupId>com.companyname.project-group</groupId>
 
    <!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
    <artifactId>project</artifactId>
 
    <!-- 项目当前版本,格式为:主版本.次版本.增量版本-限定版本号 -->
    <version>1.0</version>
 
    <!--项目产生的构件类型,
    jar、war主要用来标识项目打包出的服务是jar包还是war包 
    pom一般用作多moudle的项目中 顶层的pom用来指定子moudle中需要依赖的版本 详见2.1.3 -->
    <packaging>jar</packaging>
    
    <!--定义了本项目的名称与example的网址 -->
    <name>itoken dependencies</name>
    <url>www.funtl.com</url>
</project>

基本信息都比较易懂,主要定义了本项目的相关配置说明,例如唯一坐标、版本、项目介绍等。

2.1.2 dependencies【重要】

(1)dependencies

本元素定义了项目中所需要的相关依赖信息,也是最重要的元素之一。

xml复制代码<!--该元素描述了项目相关的所有依赖。 这些依赖自动从项目定义的仓库中下载 -->
<dependencies>
    <dependency>
        <!------------------- 依赖坐标 ------------------->
        <!--依赖项目的坐标三元素:groupId + artifactId + version -->
        <!--其中三要素的来源就是2.1.1中别人定义好的相关信息 -->
        <groupId>org.apache.maven</groupId>
        <artifactId>maven-artifact</artifactId>
        <version>3.8.1</version>
 
        <!------------------- 依赖传递 ------------------->
        <!--依赖排除,即告诉maven只依赖指定的项目,不依赖该项目的这些依赖。此元素主要用于解决版本冲突问题 -->
        <exclusions>
            <exclusion>
                <artifactId>spring-core</artifactId>
                <groupId>org.springframework</groupId>
            </exclusion>
        </exclusions>
        <!-- 可选依赖,用于阻断依赖的传递性。如果在项目B中把C依赖声明为可选,那么依赖B的项目中无法使用C依赖 -->
        <optional>true</optional>
        
        <!------------------- 依赖范围 ------------------->
        <!--依赖范围。在项目发布过程中,帮助决定哪些构件被包括进来
            - compile:默认范围,适用于所有阶段,会随着项目一起发布;  
            - runtime: 在执行时需要使用,如JDBC驱动,适用运行和测试阶段,不同于例如fastjson,需要在编译时使用;   
            - test: 只在测试时使用,用于编译和运行测试代码,例如junit,不同于junit,在发布时并不需要;    
            - optional: 当项目自身被依赖时,标注依赖是否传递。用于连续依赖时使用 -->
        <scope>test</scope>
    </dependency>
</dependencies>

(2)如何解决依赖传递问题或jar包版本冲突问题

解决上述问题一般有两种方式:

  • 当我们作为依赖服务提供者时,可以通过<optional>标签排除掉不想被传递的服务。
xml复制代码<!-- Project A -->
<project>
  ...
  <dependencies>
    <!-- declare the dependency to be set as optional -->
    <dependency>
      <groupId>sample.ProjectB</groupId>
      <artifactId>Project-B</artifactId>
      <version>1.0</version>
      <optional>true</optional> 
    </dependency>
  </dependencies>
</project>

我们的A服务中引用了外部的依赖B服务,此时有A -> B,在别人引用我们时有C -> A ->B,若此时我们A在提供对外服务时不想让别人依赖B服务,可以在引用B时添加<optional>标签为true,这样带C依赖于A时并不会引入B。如果C在此时想要使用B的相关服务,需要在C的pom中显示的调用B的相关服务。

  • 当我们作为依赖服务使用者时,可以通过<exclusions>来去除掉我们依赖包中所不想依赖的其他服务。

如果此时我们的A服务依赖于B服务,B服务依赖于C服务,则有A ->B ->C,因为某种原因例如jar包冲突,我们在A中并不想依赖于C服务的版本,可以在调用B服务时去除掉C的相关依赖,然后自己再在A中使用C的相关版本。

xml复制代码<project>
  ...
  <dependencies>
      
    <dependency>
      <groupId>sample.ProjectB</groupId>
      <artifactId>Project-B</artifactId>
      <version>1.0</version>
      <exclusions>
        <exclusion>
          <!-- 排除掉B中C的相关依赖 -->
          <groupId>sample.ProjectC</groupId>
          <artifactId>Project-C</artifactId>
        </exclusion>
      </exclusions> 
    </dependency>
      
    <!-- 自己引用C的相关版本 -->
    <dependency>
      <groupId>sample.ProjectC</groupId>
      <artifactId>Project-C</artifactId>
      <version>2.0</version>
    </dependency>
      
  </dependencies>
</project>

2.1.3 <dependencyManagement>

当一个服务中存在有多个moudle时,每个子module都可能引用了相同的jar包,但是如果将版本维护到子module的pom中,当需要升级时需要修改所有的pom文件版本。maven提供了继承的方式来解决此问题。

xml复制代码<!--在父pom中定义子pom需要的相关依赖 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

在父pom的 <dependencyManagement> 中定义子pom需要的依赖及版本。

xml复制代码  <!--在子pom中  如下定义了父pom中相关依赖信息 -->
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/>
  </parent>

  <dependencies>
        <dependency>
            <!--因为引用了父pom 所以可以不指定版本 maven会自动去父pom中查找指定版本 此处为1.0.0 -->
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
  </dependencies>

当我们的pom中有定义父pom的元素后,可以在指定需要的依赖时不指定其版本,maven会帮我们去父pom中查找相关的版本信息。

2.1.4 properties

properties主要用来定义常量,通过${value}来使用。

xml复制代码 <!--配置依赖版本-->
 <properties>
     <!-- Environment Settings -->
     <java.version>1.8</java.version>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

     <!-- Spring cloud Settings   -->
     <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
     <spring-boot-admin.version>2.0.1</spring-boot-admin.version>
     <zipkin.version>2.10.1</zipkin.version>
 </properties>

<dependencies>
        <!--spring cloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--zipkin-->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin</artifactId>
            <version>${zipkin.version}</version>
        </dependency>
    <dependencies>

此外,maven还通过约定大于配置的方式定义了一些常用的属性。

属性

定义

${basedir}

存放pom.xml和所有的子目录

${basedir}/src/main/java

项目的java源代码

${basedir}/src/main/resources

项目的资源,比如说property文件,springmvc.xml

${basedir}/src/main/webApp/WEB-INF

web应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面

${basedir}/target

打包输出目录

${project.version}

项目版本

${project.groupId}

项目groupid

2.1.5 resources

resources标签用来标识项目在编译运行时需要额外编译的文件。例如手工引入jar包、不同运行环境对应不同的profile。

xml复制代码<build>
     <resources>
         <!--首先将默认resources目录下的所有文件包括 -->
         <resource>
             <directory>src/main/resources</directory>
             <filtering>true</filtering>
             <!--只编译所有以.fxml结尾的文件 -->
             <includes>
                 <include>**/*.fxml</include>
             </includes>
             <!--排除掉所有的yaml文件 -->
             <excludes>  
                    <exclude>**/*.yaml</exclude>  
             </excludes>
         </resource>
         <!--将不同环境下对应的不同yaml或properties文件编译运行 -->
         <resource>
             <!--
             <directory>src/main/profiles/dev</directory>
             <directory>src/main/profiles/beta<directory>
             <directory>src/main/profiles/pre</directory>
             -->
             <directory>src/main/profiles/product</directory>
             <filtering>true</filtering>
             <includes>
                 <include>**/*.fxml</include>
             </includes>
         </resource>
         <!--将手工引入的jar包编译运行 -->
         <resource>
                <directory>lib</directory>
                <targetPath>BOOT-INF/lib/</targetPath>
                <includes>
                    <include>**/*.jar</include>
                </includes>
            </resource>
     </resources>
</build>

2.1.6 profile

与setting.xml中profile所不同的是(参照1.2.8),pom中的profile有着更多的标签来描述一组环境。从作用上来区分的话,一般setting.xml用来标识不同的远程仓库,而pom中的profile一般用来标识当前项目属于什么环境,如下是一组常见的pom中的profiles。

xml复制代码<profiles>
        <profile>
            <id>dev</id>
            <!--激活条件 其中默认为该profile 更多激活条件可以参考1.2.8 -->
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <!--当此profile被激活时,会将 project.active 的属性赋值为dev -->
            <properties>
                <project.active>dev</project.active>
            </properties>
        </profile>
        <profile>
            <id>test</id>
            <!--当此profile被激活时,会将 project.active 的属性赋值为test -->
            <properties>
                <project.active>test</project.active>
            </properties>
        </profile>
</profiles>

<resources>
         <resource>
             <!--根据不同的环境 project.active 取得不同的值 从而会将不同的环境下的yaml或properties文件编译进项目中 达到只需要在编译时指定环境变量即可 不用每次都要修改配置文件 -->
             <directory>src/main/${project.active}</directory>
             <filtering>true</filtering>
             <includes>
                 <include>**/*.fxml</include>
             </includes>
         </resource>
</resources>

此外,一般通过 mvn -P dev/beta/pre/product 命令来激活不同的环境,也可以通过其他的方式来激活profile,详见1.2.10。

当然,pom中的profile不止有指定编译环境的功能,同样也可以指定远程仓库等其他功能。

2.1.6 modules

当我们项目中有多个模块时,如果我们要单独打包的话需要在每一个模块都执行对应的maven命令,但是通过<modules>标签可以将子服务或平级服务进行聚合,只需要打包该服务,也就会将其对应的子模块同时打包。

xml复制代码<modules>
    <!-- 引入子模块所在的相对目录 -->
    <module>springMyBatis</module>
    <!-- 引入同级模块所在的相对目录 -->
    <module>../utils</module>
 </modules>

2.1.7 plugins

TODO

三、null:jrdp-common:null:jar问题解决

3.1包找不到问题

我们某次在开发功能的时候,在我们的项目中引用了伏羲另外一个系统的jar包,但是预发环境下编译的时候却发现构建失败,原因是

 

因为当时项目有用到京东自己的仓库,所以我们当时第一反应是去仓库中查找,结果发现仓库中是有这个jar包的。

在发现并不是最常见的jar包不存在的问题后,我们开始分析报错原因,发现报错的 jrdp-common这个包并不是我们直接引用的,而是在我们引用的jar包中引用的,并且 null:jrdp-common:null:jar可以推测前面应该是 groupID与 version。

假设我们的项目是A项目,引用的项目是B项目,也就是 A->B->jrdp-common

于是我们打开B项目,查看B的pom结构。

发现B项目的pom中确实引用了jrdp-common这个包,但是并没有指定版本,是因为继承了 xx-parent这个包,我们在这个包中确实也找到了指定的版本号,因此就排除了项目中没有指定groupid与版本号的问题。

这个时候好像就问题就陷入了思路,但是我们注意到,我们之前另一个私服上也是有这个包的,那么之前的在另一个私服上引用好像是没有问题的,我们查看了私服了上的pom文件,发现也是跟项目一样的。

然后我们就突然想到,会不会是仓库中的包有问题,果不其然,没有指定parent也没有指定版本号,于是我们修改了这个版本的pom,至此问题解决。

总结:因为我们的系统已经被好多个团队中转接手过,因此可能在某些地方踩了不少坑,像这种问题应该就是之前的团队上传了一些有问题的jar包,导致依赖不可用,但是因为我们之前一直用的私服是没有什么问题的,只到这次用到了仓库问题才浮现。

另外,此问题并不具有普适性,但是当遇到了groupid不能未空的时候可以按照此方法进行排查。

作者:京东科技 韩国凯

内容来源:京东云开发者社



Tags:Maven   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
使用Maven和Tomcat创建并部署Java应用
引言本文分享使用Maven和Tomcat创建、构建和部署简单的Java Web应用程序的过程。Maven是个强大的构建自动化工具,可以帮助管理项目依赖关系并自动化构建任务,而Tomcat是被广泛...【详细内容】
2023-12-11  Search: Maven  点击:(171)  评论:(0)  加入收藏
教你如何选择Gradle和Maven
在软件开发领域,构建工具对于自动化编译、测试和打包代码过程至关重要。Gradle 和 Maven 是当今最流行的两种构建工具,每种工具都有自己的一套功能和优点。Gradle 是一种现代...【详细内容】
2023-10-08  Search: Maven  点击:(295)  评论:(0)  加入收藏
聊聊 Maven 最佳实践
本文翻译自国外论坛 medium,原文地址:https://medium.com/@AlexanderObregon/maven-best-practices-tips-and-tricks-for-java-developers-438eca03f72bMaven 简介Maven 是一...【详细内容】
2023-09-09  Search: Maven  点击:(325)  评论:(0)  加入收藏
10个Java项目中必备的Maven插件
介绍Maven是Java开发人员必备的构建工具和项目管理工具。它采用简单的项目对象模型(POM),实现Java应用程序的自动化构建、测试和部署。使用Maven插件,开发人员能够进一步扩展工...【详细内容】
2023-08-17  Search: Maven  点击:(320)  评论:(0)  加入收藏
Maven进阶学习指南
前言当我们在开发项目时,有时需要用到外部依赖组件,例如当我们需要Json序列化的时候需要用到FastJson组件,我们可以通过下载对应jar包加载到项目中。但当一个大的项目同时需要...【详细内容】
2023-06-25  Search: Maven  点击:(272)  评论:(0)  加入收藏
java开发人你用的maven构建还是gradle
Gradle和Maven都是Java项目构建工具,它们之间最主要的区别在于以下几点: 构建脚本语言:Maven使用XML作为构建脚本语言,而Gradle使用Groovy语言,Groovy是一种基于Java的动态语言。...【详细内容】
2023-05-16  Search: Maven  点击:(450)  评论:(0)  加入收藏
记一次内网maven私服初始化方法
背景 纯内网环境,无法连互联网 待初始化maven私服在内网,无法通过配置代理完成初始化 通过nexus部署私服,初始化maven依赖和npm依赖初始化思路 通过nexus管理端上传组件功能上...【详细内容】
2023-02-27  Search: Maven  点击:(270)  评论:(0)  加入收藏
Maven从入门到精通 pringBoot搭建SSM项目的完整过程 !
01、新建一个springboot项目 选择依赖和版本 SpringBoot整合lomboklombok是一个快速生成javabean的一个工具工程和生成日志的工具。未来给我们提供了很大的遍历。需要在ide...【详细内容】
2023-02-09  Search: Maven  点击:(183)  评论:(0)  加入收藏
安全同学讲Maven间接依赖场景的仲裁机制
一 背景为什么想写此文去年的Log4j-core的安全问题,再次把供应链安全推向了高潮。在供应链安全的场景,蚂蚁集团在静态代码扫描平台-STC和资产威胁透视平台-哈勃这2款产品的联...【详细内容】
2022-09-22  Search: Maven  点击:(339)  评论:(0)  加入收藏
Maven依赖的作用域你到底用对了没有
Maven是目前Java开发主要使用的依赖管理构建工具之一,但是很多人在引用依赖的时候直接引用坐标,而没有考虑依赖的作用范围,结果导致出现这样和那样的问题。今天胖哥就来说明一...【详细内容】
2022-07-26  Search: Maven  点击:(406)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(6)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(13)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(9)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(11)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(9)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(6)  评论:(0)  加入收藏
站内最新
站内热门
站内头条