Maven基础整理

前言

本文以当前最新发布版maven-3.6.3为例.酌情参考.

仓库

得益于坐标机制,任何Maven项目使用任何一个构建的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件。这个统一的位置就是仓库。实际的Maven项目将不再各自存储其依赖文件,它们只需要声明这些依赖的坐标,在需要的时候Maven会自动根据坐标找到相应的构件。

分类

Maven仓库的分类不能一概而论,根据不同的角度可以大致罗列如下:

位置分类

Maven实战》中将仓库分为:

本地仓库主要起缓存作用,默认在~/.m2/repository

可以通过~/.m2/settings.xml中的<localRepository>修改。

发布分类

根据是否发布,可以分为SNAPSHOTRELEASE两类。

性质分类

私服Nexus中将仓库分类为hostedgroupproxy

hosted

一般只包含SNAPSHOTRELEASE两类。因此Nexus内置了maven-snapshotsmaven-releases

group

顾名思义就是组,可以自由组合,类型不限。Nexus内置了maven-public

proxy

proxy只能一次代理一个目标仓库。一般都是将生成某一类proxy地址归并到一个group中,形成类似Nginx反向代理的效果。

proxy类型应该是私服用的最多的类型。

Nexus内置了maven-central,它默认代理的是https://repo1.maven.org/maven2/

结构

Maven的目录结构看起来比较简单,包括:

├── bin
├── boot
├── conf
│   └── logging
└── lib
    ├── ext
    └── jansi-native
        ├── freebsd32
        ├── freebsd64
        ├── linux32
        ├── linux64
        ├── osx
        ├── windows32
        └── windows64

bin

bin主要存放命令行的执行脚本,包括mvnmvnDebug。此外还有m2.conf文件.这是classworlds的配置文件.

boot

只有一个文件plexus-classworlds-x.x.x.jar,是一个类加载器框架.一般不用关心.

conf

配置文件夹.包含最重要的settings.xml.

lib

用以存放maven运行时需要的Java类库.包含maven自身及第三方依赖.

在当前版本3.6.3中,该目录下的maven-model-builder-3.6.3.jarorg.maven.model下存放了全局的pom-4.0.0.xml,它定义了默认的central仓库链接:https://repo.maven.apache.org/maven2.

坐标

项目坐标

指的是pom.xml最外一层的xml元素,常用的包括groupId,artifactId,version,packaging,dependencyManagementbuild.

packaging

该元素定义Maven项目的打包方式.首先,打包方式通常与所生成构件的文件扩展名称对应.默认为jar,可供选类型包括但不限于jar,war,earpom.之所以不限是因为打包插件可以自定义类型.

依赖坐标

依赖坐标是最最常用的元素了,这里指对应pom.xml<dependency>.

包括groupId,artifactId,version,classifier,typeexclusions.

type

一个依赖坐标对应的文件格式一般为$artifactId-$version-$classifier,扩展名一般是$type,它的默认值是jar,不过尽管它通常表示依赖项文件名的扩展名,但并非总是如此。 可以将类型映射到其他扩展名和classifier。 这种类型通常对应于所使用的包装,尽管并非总是如此。 可供选项包括jarwarejb-clienttest-jar。 可以通过将扩展名设置为true的插件来自定义新类型,因此这不是完整列表。

classifier

帮助定义构件输出的一些附属构件.比如文档和源代码.对应的classifier就分别是javadocsources.

另外,常用的选项还有pom,通常表示一些父依赖.

比较经典的应用是net.sf.json-lib:json-lib,必须指定classifier才可正常工作.

    <dependency>
      <groupId>net.sf.json-lib</groupId>
      <artifactId>json-lib</artifactId>
      <version>2.4</version>
      <classifier>jdk15</classifier>
    </dependency>

注意:

不能直接定义项目的classifier因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成.

scope

依赖范围:

依赖范围编译测试运行例子
compileYYYspring-core
test-Y-Junit
providedYY-servlet-api
runtime-YYoracle-jdbc驱动
systemYY-本地的,Maven仓库之外的类库文件
<systemPath>搭配使用
optionalYNN可选,并且不会被传递依赖
1. spring-boot-devtools
2. 比如有个持久层隔离包,里面有多个数据库驱动程序,它们是互斥的,放在pom中供调用者参考.

配置文件

$MAVEN_HOME/conf/settings.xml是全局配置文件。一般不建议修改

当我们初次执行操作,如mvn help:system时会创建~/.m2repository目录,同时生成空的settings.xml用以存放用户级别的配置。用户根据需要修改~/.m2/settings.xml

<settings>
  <!-- 本地仓库位置,默认路径: ${user.home}/.m2/repository -->
  <localRepository>/path/to/local/repo</localRepository>
  <!-- 交互模式,默认开启  -->
  <interactiveMode>true</interactiveMode>
  <!-- 离线模式,默认关闭-->
  <offline>false</offline>
  <!-- 插件组-->
  <pluginGroups> </pluginGroups>
  <!-- 代理-->
  <proxies></proxies>
  <!-- 认证 -->
  <servers></servers>
  <!-- 镜像-->
  <mirrors> </mirrors>
  <!-- 环境 -->
  <profiles> </profiles>
  <activeProfiles> </activeProfiles>
</settings>

localRepository

用以指定本地仓库位置.

pluginGroups

当插件的组织Id(groupId)没有显式提供时,供搜寻插件组织Id(groupId)的列表。该元素包含一个pluginGroup元素列表,每个子元素包含了一个组织Id(groupId)。当我们使用某个插件,并且没有在命令行为其提供组织Id(groupId)的时候,Maven就会使用该列表。默认情况下该列表包含了org.apache.maven.pluginsorg.codehaus.mojo.

proxy

有时候所在公司基于安全考虑,要求使用通过安全认证的代理访问互联网.这时候就需要为其配置HTTP代理,才能正常访问和下载.可供配置标签:

<proxies> 
	<proxy>
      <!-- 可选 -->
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
</proxies>

proxies下可以有多个proxy元素,如果声明了多个,则默认情况下第一个被激活的会生效.

nonProxyHosts用来指定哪些主机不需要被代理.可以使用|符号来分隔多个,同时支持通配符*,比如*.aliyun.com.

servers

用来配置远程仓库的认证信息,其中serveridpom.xml中的repositoryid一一对应.

    <servers>
        <server>
            <id>swb</id>
            <username>admin</username>
            <password>XXXX</password>
            <privateKey>~/.ssh/id_rsa</privateKey>
            <!--私钥密码-->
            <passphrase>XXX</passphrase>
        </server>
    </servers>

mirrors

有些仓库在国内访问速度较慢,因此会出现一些镜像仓库,如阿里云等,mirrors就是用来配置镜像仓库提高效率。
以公有仓库为例:

    <mirror>
      <id>aliyun</id>
      <mirrorOf>public</mirrorOf>
      <name>aliyun</name>
      <url>https://maven.aliyun.com/repository/public</url>
    </mirror>

由上面的分类可知,Maven有好几种类型仓库
这里的mirrorOf支持通配符*,代表所有类型,如果有私服的话,可以只配一个镜像地址,省事儿,比如我们的私服结构如下:

public代理了绝大部分常用仓库,而且非常容易扩展,此时使用<mirrorOf>*</mirrorOf>就非常合适。

profiles

类似springboot中的profile,针对不同的环境开启不同的配置,比如在settings.xml中设置统一的默认编译版本:

    <profiles>
        <profile>
            <id>java8</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>1.8</jdk>
            </activation>
            <properties>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
                <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
            </properties>
        </profile>
        <profile>
            <id>java9</id>
            <activation>
                <activeByDefault>false</activeByDefault>
                <jdk>9</jdk>
            </activation>
            <properties>
                <maven.compiler.source>9</maven.compiler.source>
                <maven.compiler.target>9</maven.compiler.target>
                <maven.compiler.compilerVersion>9</maven.compiler.compilerVersion>
            </properties>
        </profile>
    </profiles>

    <activeProfiles>
        <activeProfile>java8</activeProfile>
<!--        <activeProfile>java9</activeProfile>-->
    </activeProfiles>

除此之外,还可以通过命令行-P参数指定:mvn compile -Pjava8.

pom.xml

就像makeMakefile,Antbuild.xml一样,pom.xmlMaven项目的核心.

<?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>org.example</groupId>
    <artifactId>maven</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies></dependencies>
</project>

repositories

有些情况下,默认的中央仓库无法满足项目的需求,可能项目需要的构件存在于另外一个远程仓库中,如JBoss Maven.这时,可以在POM中配置该仓库.

repositories元素下,可以使用repository子元素声明一个或者多个远程仓库.比如在超级POM(位于maven-model-builder-3.6.3.jar)中默认的仓库配置:

  <repositories>
    <repository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

使用的idcentral,如果其他仓库声明的id也一样,就会覆盖中央仓库的配置.

这里的snapshots比较重要,定义了是否提供快照版本下载支持.对应的还有releases,它们都是对应的xsd规定的RepositoryPolicy类型,默认为真.

RepositoryPolicy还包括updatePolicychecksumPolicy,表示更新频率和文件校验策略.

updatePolicy

默认的值是daily,表示每天检查一次.其他可用的值包括:

  1. never-从不检查更新
  2. always-每次构建都检查更新
  3. interval: X-每隔X分钟就检查一次更新,X为任意整数.

checksumPolicy

  1. warn,默认值,校验不通过给出提示
  2. ignore,忽略
  3. fail,校验不通过构建失败.

layout

表示布局,两个可选项:legacydefault.

legacy对应的是maven1的布局,一般不用.

default对应的是maven2maven3的布局,是目前来说最常用的,因此也是默认值.

认证

有时出于安全考虑,需要认证后提供下载和部署服务,比如很多公司内部的私服不允许匿名拉取文件的.

但认证信息在settings.xmlservers的子元素server中配置,通过id与此处的repositoryid一一对应.

其他Maven配置

除了全局的settings.xml和项目级别pom.xml,还有其他一些配置,比如MAVEN_OPTS,项目根目录下的.mvn文件夹.具体参考官网configure

生命周期

Maven拥有三套相互独立的生命周期:clean,defaultsite.

每个生命周期都包含多个阶段(phase),这些阶段是有顺序的.

clean

目的是清理项目,包含三个阶段:.

阶段可执行
pre-cleanF
cleanT
post-cleanF

default

定义了真正构件时所需要执行的所有步骤,它是所有生命周期中最核心的部分.它包含的阶段较多,这里仅仅列举一些关键阶段,它们均可在命令行显式执行:

  • validate 验证项目是否正确并且所有必要的信息均可用
  • compile
  • test
  • package
  • verify 对集成测试的结果进行任何检查,以确保符合质量标准
  • install 将生成的包安装到本地存储库中,以作为本地其他项目中的依赖项
  • deploy 在构建环境中完成后,将最终程序包部署到远程存储库,以便与其他开发人员和项目共享。

site

目的是建立和发布项目站点,Maven能够基于POM所包含的信息,执行mvn site命令会自动生成一个友好的站点,位于target/site目录下的静态页面,包含如下阶段:

  • pre-site
  • site
  • post-site
  • site-deploy 将生成的项目站点发布到服务器上

本生命周期不常用,用到再补充.

插件

为了达到开箱即用的效果,Maven内置了许多常用插件:

namephasemore details
maven-clean-plugincleanclean_Lifecycle
maven-resources-plugindefaultdefault-bindings
maven-compiler-plugin
maven-surefire-plugin
maven-jar-plugin
maven-install-plugin
maven-deploy-plugin
maven-site-pluginsitesite_Lifecycle

插件目标

Maven的核心仅仅定义了抽象的生命周期,具体的任务是交给插件完成的,插件为了功能复用,又将其分为多个目标.

比如maven-compiler-plugin包含三个目标:

GoalDescription
compiler:compile绑定到编译阶段,用于编译主要源文件。
compiler:helpmaven-compiler-plugin上显示帮助信息。 调用mvn compiler:help -Ddetail=true -Dgoal=<goal-name>以显示参数详细信息。
compiler:testCompile绑定到测试编译阶段,用于编译测试源文件。

如果想查看其他插件目标,直接访问官网,找到想了解的插件,查看:

插件绑定

Maven的生命周期与插件相互绑定,用以完成实际的构建任务.

具体而言,是生命周期的阶段与插件目标相互绑定.

内置绑定

为了让用户几乎不用任何配置就能构建Maven项目,所以内置了一些绑定,官网中给出了一系列对应关系.

其中cleansite阶段的绑定关系比较简单,直接列举出来了,default阶段的默认绑定比较复杂,是根据打包类型对应的,可以查看其给出的default-bindings.

POM配置

Maven帮我们内置了一些插件,一般足够满足我们的需求,只有在很少的情况下,项目使用的插件无法在中央仓库找到,或者自己编写了插件,就必须考虑配置其他的远程插件仓库了.

pom.xml为我们提供了条件:

使用pluginRepositories+pluginRepository,其他配置则与repositories+repository相同.

命令行

命令行可以直接激活生命周期阶段,也可以调用插件目标.

生命周期调用

mvn clean
mvn test

mvn clean install
mvn clean deploy site-deploy

插件目标调用

有些任务不适合绑定在生命周期上,所以Maven提供了调用插件目标的方式:

mvn help:describe -Dplugin=compiler -Ddetail
mvn dependency:tree

describe是插件目标没错,那help是什么呢,其实它是目标前缀,是Maven为了简化调用而设定的.它的完整命令是:

mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:describe -Dplugin=compiler -Ddetail

其中版本号可选.第二条命令同理.

插件前缀

一般的前缀与它的插件名称对应:maven-XXX-plugin,如果不一样的话,以groupIdorg.apache.maven.plugins的官方插件为例,可以通过远程仓库maven-metadata.xml查看所有插件的前缀.

聚合与继承

聚合

对于复杂的Maven项目,一般建议采用多模块的方式来设计开发,便于后期维护管理。但是构建项目时,如果每次都需要按模块一个一个进行构建会十分麻烦,而Maven的聚合功能就可以很好的解决这个问题,当用户对聚合模块执行构建任务时,会对所有被其聚合的模块自动地依次进行构建任务.

一个聚合项目,其packaging必须定义为pom.然后使用modules列举其他模块.

继承

父模块packaging必须为pom.

主要涉及的元素是properties,dependencyManagementpluginManagement.

properties

预置属性,子模块可以直接使用.

dependencyManagement

定义一些可能涉及的不冲突的依赖,子模块在使用相同的依赖时,可以忽略版本,防止冲突.

子模块可以选择不使用,显式配置依赖时才会生效.

当子模块定义了不同的版本时,会覆盖.

pluginManagement

dependencyManagement起相同作用.

公共依赖

如果有公共依赖,可以提取到这里,以便统一管理.

可继承元素

上述properties,dependencyManagementpluginManagement都是可继承元素,但这不是全部.

所有可继承元素:

  • groupId
  • version
  • description
  • organization
  • inceptionYear 创始年份
  • url
  • developers
  • contributors
  • distributionManagement 部署配置
  • issueManagement 缺陷跟踪
  • ciManagement 持续集成(Continuous Integration)管理
  • scm 版本控制信息
  • mailingLists 邮件列表信息
  • properties
  • dependencies
  • dependencyManagement
  • pluginManagement
  • repositories
  • build
  • reporting

通过parent引入就可以享受父模块提供的便利.

parent中有一个relativePath元素用于指定父模块的pom.xml位置,默认值是../pom.xml,如果不符合这种结构,可以另行指定.

import 依赖范围

我们在一个新的项目/模块中,如果期望复用其他POMdependencyManagement元素的所有配置,我们可以通过继承或拷贝该配置实现.

因为maven只支持单继承,如果当前项目已经继承了一个父模块,此时即可通过import导入的方式来复用其他POM中的dependencyManagement配置。需要注意的是,由于import依赖范围的特殊性,其一般指向的是打包类型为pom的模块。故其type只能为pom.

    <parent>
        <groupId>com.yan</groupId>
        <artifactId>xxx-parent</artifactId>
        <version>xxxx</version>
        <relativePath/>
    </parent>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.1.4.RELEASE</version>
                <!-- 必须是pom -->
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

聚合与继承的比较

  1. 不同
  • 目的不同,聚合是为了集成构建,继承是为了消除重复
  • 聚合知道被聚合的模块,反之不知;继承则是子知父,父不知子
  1. 相同
  • packaging均为pom,这意味着项目可以同时是聚合模块和父模块.
  • 都为构建提供方便

反应堆

就是决定构建顺序的构建结构.

单模块的反应堆就是它本身,如果有父模块,先构建父模块.

如果是聚合模块,那么module定义的顺序决定了构建顺序.

灵活构建

大多数时候,用户都是一次性构建整个项目,但有些时候用户可能仅仅需要构建某个模块,即用户需要裁剪反应堆来实现构建指定模块的目的。在Maven命令行中可通过相关选项参数实现反应堆的裁剪

  1. -pl,projects:构建指定模块,多个模块用逗号进行分隔

    mvn package -pl m_a, m_b
    
  2. -am,-also-make:同时构建所列模块的父模块

    mvn package -pl m_a -am 
    
  3. -amd,-also-make-dependents:同时子模块

    mvn package m_parent -amd
    
  4. -rf,-resume-from:从指定模块开始构建

    mvn -rf
    

Maven属性

内置属性

主要有两个常用内置属性:

  • basedir:项目根目录,即包含pom.xml的目录
  • version:表示项目版本

POM属性

  • project.build.sourceDirectory:项目的主源码目录,默认为src/main/java.
  • project.build.testSourceDirectory:项目的测试源码目录,默认为src/test/java.
  • project.build.directory:项目构建输出目录,默认为target.
  • project.outputDirectory:项目主代码编译输出目录,默认为target/classes
  • project.testOutputDirectory:项目测试代码编译输出目录,默认为target/testclasses
  • project.groupId
  • project.artifactId
  • project.version
  • project.build.finalName:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}

自定义属性

用户可以在POMproperties元素下自定义属性

settings属性

可以用以settings.开头的属性引用settings.xml中的值,如settings.localRepository指向本地仓库的地址.

Java系统属性

所有Java系统都可以使用Maven属性引用.使用mvn help:system查看所有的Java系统属性.

环境变量属性

所有环境变量都可以使用以env.开头的Maven属性引用.使用mvn help:system查看所有的环境变量属性.

资源过滤

Maven属性默认只有在POM中才会被解析,因此有时会需要让Maven解析资源文件(位于src/main/resources)中的Maven属性.

资源文件的处理需要通过maven-resources-plugin,它默认的行为只是将项目主资源文件复制到主代码编译输出目录中,将测试资源文件复制到测试代码编译输出目录中.若要开启资源过滤,需要配置<filtering>为真:

            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <resources>
                        <resource>
                            <directory>${project.build.sourceDirectory}</directory>
                            <filtering>true</filtering>
                        </resource>
                    </resources>
                </configuration>
            </plugin>

ToolChains

默认路径是~/.m2/toolchains.xml.

工具链是一个预配置的对象,Maven插件可将其用于工具配置检索(位置和其他信息)。

最常用的就是Jdk版本的选择,还提供了自定义工具链的方法.

使用工具链,需要配置两个基本组件:

  1. 项目POM中的maven-toolchains-plugin
  2. 本机上的toolchains.xml文件。

实战

Jdk版本工具链示例:

  1. ~/.m2/toolchains.xml中:

    <?xml version="1.0" encoding="UTF8"?>
    <toolchains>
      <!-- JDK toolchains -->
      <toolchain>
        <type>jdk</type>
        <provides>
          <version>1.5</version>
          <vendor>sun</vendor>
        </provides>
        <configuration>
          <jdkHome>/path/to/jdk/1.5</jdkHome>
        </configuration>
      </toolchain>
      <toolchain>
        <type>jdk</type>
        <provides>
          <version>1.6</version>
          <vendor>sun</vendor>
        </provides>
        <configuration>
          <jdkHome>/path/to/jdk/1.6</jdkHome>
        </configuration>
      </toolchain>
    
      <!-- other toolchains -->
      <toolchain>
        <type>netbeans</type>
        <provides>
          <version>5.5</version>
        </provides>
        <configuration>
          <installDir>/path/to/netbeans/5.5</installDir>
        </configuration>
      </toolchain>
    </toolchains>
    
  2. pom.xml

    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-toolchains-plugin</artifactId>
        <version>1.1</version>
        <executions>
          <execution>
            <goals>
              <goal>toolchain</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <toolchains>
            <jdk>
              <version>1.5</version>
              <vendor>sun</vendor>
            </jdk>
          </toolchains>
        </configuration>
      </plugin>
    </plugins>
    

    3.3.1版本开始可以使用--global-toolchains file来指定toolchains的配置位置,但还是强烈建议放在默认目录~/.m2下.

可执行jar包

maven-jar-plugin打的jar包不可执行,需要借助maven-shade-plugin来配置:

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.0</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>${你的主类全类名}</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

更多信息参考:maven-shade-plugin.

部署

pom.xml中加入

    <distributionManagement>
        <!--release仓库-->
        <repository>
            <id>swb</id>
            <url>http://repo.cnswb.com/repository/maven-releases/</url>
        </repository>
        <!--快照仓库-->
        <snapshotRepository>
            <id>swb</id>
            <url>http://repo.cnswb.com/repository/maven-snapshots/</url>
        </snapshotRepository>
    </distributionManagement>

执行命令mvn clean deploy,如果当前项目版本为快照版本则发布至snapshotRepository,否则是repository.

升级

普通升级都是从官网下载最新安装包到本机解压后从新指定环境变量PATH

但还有更好的方式,即创建一个软连接用来统一代理,这样每次只需改变软连接的目标地址实现升级了。

/opt/apache目录下创建一个软连接maven,用它来指向想要升级的Maven目录,环境变量中的M2_HOME设置为/opt/apache/maven

比如从当前为3.6.1

ln -s /opt/apache/apache-maven-3.6.1  /opt/apache/maven

升级为3.6.3

rm -rf /opt/apache/maven
ln -s /opt/apache/apache-maven-3.6.3  /opt/apache/maven

升级后查看:

yan@yan-PC:/opt$ ll /opt/apache
总用量 16
drwxr-xr-x  4 yan yan 4096 3月  25 14:37 .
drwxr-xr-x 26 yan yan 4096 3月  25 10:52 ..
drwxr-xr-x  6 yan yan 4096 3月  25 14:37 apache-maven-3.6.1
drwxr-xr-x  6 yan yan 4096 3月  25 10:57 apache-maven-3.6.3
lrwxrwxrwx  1 yan yan   30 3月  25 10:56 maven -> /opt/apache/apache-maven-3.6.3
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值