把Maven的架构,用法,坑点你都需要知道

    xiaoxiao2022-06-30  123

    没有Maven之前的日子

    个人的一个小感受,学习一个新技术,应该以前史的眼光开看待这个新技术出现的原因,以及帮咱们处理了什么问题。咱们来回想一下没有Maven的日子是怎么样的?

    开发一个项目,需求用他人写好的jar包,咱们先把开源的jar包下载下来放到项目的lib目录下,并把这个目录添加到CLASSPATH(告诉Java实施环境,在哪些目录下可以找到你要实施的Java程序需求的类或许包)

    咱们下载了a.jar发现a.jar还需求依托b.jar,成果又去把b.jar包下载下来运行

    假如命运够好,咱们的项目在添加完一切的依托后,能正产工作了。假定命运差点,还会遇到版其他问题,例如a.jar在调用b.jar的时分发现b.jar根柢没有这个方法,在其他版别中才有,现在好了,光找依托和适配版别就能花上不少时间

    并且咱们往git上上传代码的时分,还有必要把这些lib都上传上去。他人下载咱们的代码时也有必要把lib下载下来,这个真心心的消耗时间

    这时分Maven作为Java国际的包管理工具出现了,当然Java国际还有其他包管理工具,例如gradle等。就像yum是Linux国际的包管理工具,webpack是前端国际的包管理工具一样

    Maven仓库的种类

    Maven找jar包的过程是这样的,先在本地仓库找,找不到再去私服(如果配置了的话),再找不到去中央仓库(http://repo1.maven.org/maven2/,maven团队负责维护) 从中央仓库找到后,会在私服和本地仓库放一份,从私服找到后也会在本地仓库放一份

    当你安装在好了Maven以后,在conf目录下有个settings.xml文件,这个里面配置的项很多,后文会详细介绍这个配置文件。

    <!-- localRepository | The path to the local repository maven will use to store artifacts. | | Default: ${user.home}/.m2/repository <localRepository>/path/to/local/repo</localRepository> -->

    在这个配置文件下有这样一句话,说了Maven默认的本地仓库地址为${user.home}/.m2/repository(当然你可以重新设置本地仓库的地址,上面就是模板),我是window电脑,来看看这个目录

    看到有很多jar包被存到本地,当然如果你想配置私服也是在settings.xml上进行配置,随便一搜很多教程,不再赘述 搭建私服好处多多,在一个公司内部可以开发一些公共的基础组件放到私服上,方便其他同事使用

    Maven的默认配置

    一个Maven的项目的整体结构是这样的

    为什么一个Maven项目的文件结构是这种的呢? 这就不得不说到Maven的一个特性,约定优于配置。 Maven默认配置了${project.basedir}/src/main/java为项目的源代码目录 ${project.basedir}/src/main/test为项目的测试代码目录 ${project.basedir}/target为项目的编译输出目录等

    spring boot就是约定优于配置的体现,想想我们用spring mvc的时候还得配置视图解析器,包的自动扫描,而用了spring boot框架,我们就完全不用再配置了

    Maven项目详解

    安装还是挺简单的,我就不再介绍,我也没有单独下载,一般就用了Idea自带的Maven了,下载完后目录结构如下:

    bin目录: 该目录包含了mvn运行的脚本,这些脚本用来配置java命令,准备好classpath和相关的Java系统属性,然后执行Java命令。

    boot目录: 该目录只包含一个文件,该文件为plexus-classworlds-2.5.2.jar。plexus-classworlds是一个类加载器框架,相对于默认的java类加载器,它提供了更加丰富的语法以方便配置,Maven使用该框架加载自己的类库。

    conf目录: 该目录包含了一个非常重要的文件settings.xml。直接修改该文件,就能在机器上全局地定制maven的行为,即对所有用户都生效。一般情况下,我们更偏向于复制该文件至/.m2/目录下(表示用户家目录,windows下~就是C:UsersPeng,Peng是小编的用户名),然后修改该文件,在用户级别定制Maven的行为。

    lib目录: 该目录包含了所有Maven运行时需要的Java类库,Maven本身是分模块开发的,因此用户能看到诸如maven-core-3.0.jar、maven-model-3.0.jar之类的文件,此外这里还包含一些Maven用到的第三方依赖如commons-cli-1.2.jar、commons-lang-2.6.jar等等。、

    settings.xml配置文件详解

    我们来详细说一下settings.xml这个文件,这个文件可以定制Maven的行为,上面已经说到settings.xml可以放在2个位置,~/.m2/setting.xml(默认没有,需要我们自己复制)和${maven.home}/conf/setting.xml

    这2个配置文件的加载顺序为~/.m2/setting.xml>${maven.home}/conf/setting.xml,为了不影响他人,所以我们将conf下的settings.xml复制到家目录,在用户级别定制Maven的行为。

    这个和配置环境变量有点类似,Windos和Linux都可以配置系统级别的环境变量和用户级别的环境变量,这里单说一下Linux的吧,在/etc/profile里面配置的就是系统级别的环境变量,在~/.bash_profile里面配置的就是用户级别的环境变量

    各种配置项还是挺多的,设置镜像仓库(国内用阿里云的比较多),设置代理,不再赘述

    maven常用命令 命令描述mvn -version显示版本信息mvn clean删除target目录mvn compile编译src/main/java下的源代码mvn package打包,在target下生成jar包或者war包mvn test执行src/test/java下以Test开头或者以Test结尾的类的测试用例mvn install打包,并把jar包或者war包复制到本地仓库,供其他模块使用mvn deploy将打包的文件发布到私服mvn dependency:tree打印出项目的整个依赖树

    当然也可以连着使用 mvn clean package 清理打包 mvn clean package -DskipTests=true 清理打包,并跳过测试用例 mvn clean install 清理打包,并将jar包或者war包复制到本地仓库

    运行单测的时候也没必要一个一个点测试方法,mvn test 一个命令跑完所有测试用例, 要注意的是只会执行以Test开头或者结尾的测试类,也没必要自己写测试类,我在推荐阅读第一篇文章中演示了快速生成测试类的方法,可以去看看,生成的测试类都是以Test结尾的

    mvn dependency:tree > show.txt 将依赖输出重定向到文件中,方便查看

    pom.xml详解

    groupId 公司域名倒过来 artifactId 功能命名 version 版本号

    这三个维度确定一个jar包,就像用(x,y,z)坐标在三维空间中唯一确定一个点。

    packaging 打包方式,jar,war,maven-plugin(开发maven插件)

    scope详解 参数解释是否会被打入最终的jar包compile默认的scope是test测试使用否provided编译需要否runtime编译不需要,运行时需要(接口与实现分离)是system加载本地jar否

    类似如下这种,没有指定scope,说明scope是compile

    <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>

    test是指在运行测试用例的时候才会用到,没必要打入到最后的jar里面,所以你看到的测试框架的scope基本上都是test

    org.springframework.boot spring-boot-starter-test test provided,编译的时候会用到,但不会被打入最后的jar包

    例如想把spring boot项目以war包的形式放在tomcat中运行,首先得加入如下依赖

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>

    或者你写了一个放在Storm集群或者Flink集群上运行的任务,最后都要把Storm的依赖或者Flink的依赖设置成provided,因为集群上已经都有这些环境的jar包、

    如果你用到lombok插件的话,你会发现lombok的Maven是如下形式,说明它只会编译的时候会用到。

    <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> <scope>provided</scope> </dependency>

    我写了如下一个测试类

    @Data public class Test { private String name; private int age; }

    生成的class文件反编译后的如下,验证了我们的想法,编译之后确实没有必要再用lombok这个jar包

    public class Test { private String name; private int age; public Test() { } public String getName() { return this.name; } public int getAge() { return this.age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } }

    runtime,运行时才会用到。例如,如果你的项目有对数据库的操作,但没有加入相应的JDBC的实现jar包,如mysql-connector-java,是可以编译成功的,只有运行时才会报错。所以你看到的JDBC实现的jar包scope为runtime,表明这个jar包在运行时才会用到

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

    system,本地加载jar,当你和第三方公司合作,他们只是给了你一个jar包时,你可以有三种选择

    mvn install到本地仓库

    mvn deploy到私服

    指定jar包路径,从本地加载,例如如下pom形式

    com.tievd.third arcvideo 1.0 system ${basedir}/lib/face-api-1.0.jar

    前文已经说到scope为system的依赖不会被打入最终的jar包,得通过配置插件等方式将依赖打入最终的jar包,所以这种方式一般很少使用。

    Maven jar包冲突如何解决?

    依赖传递

    假设我们现在有一个多模块项目,依赖关系如图,我们在st-web模块中引入st-dal依赖时,st-common-lib这个依赖也会被我们引入,这个就是依赖传递,下表中列出了scope在依赖过程中发生的变化,列标题为被依赖的模块的scope

    依赖仲裁

    依赖仲裁就是当项目中引入的jar包,groupId (公司域名倒过来)和artifactId (功能命令)一样,但是version不一样,应该选用哪一个version?也经常被人叫做依赖冲突

    最短路径原则

    假如说我们现在的项目依赖关系如图?那么maven会选用st-common-lib的那个版本呢?

    答案是1.1这个版本,st-web到st-common-lib(1.1)的距离为1,st-web到st-common-lib(1.0)的距离为2,选择距离短的,即最短路径原则

    如何看依赖的距离关系呢?前文说过,执行如下命令打印出全局的依赖树,层级关系特别清楚

    mvn dependency:tree > show.txt

    声明优先原则

    项目依赖如图,路径一样,会选用st-common-lib的哪个版本呢?这就得看你在pom文件中先声明是哪个依赖,如果在pom.xml中,st-remote-invoker写在前面,就会用1.0这个版本,如果st-dal写在前面,则会用1.1这个版本

    依赖排除

    去掉间接引入的jar包

    如不想用spring boot默认提供的log,想集成第三方的log时,或者说上面依赖仲裁的第二个例子中,只想用st-common-lib的1.1版本,就可以把1.0版本排除掉

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency>

    (想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程等待你来学习。完全免费哦! )


    最新回复(0)