Maven基础篇 | 我的日常分享

Maven基础篇

Maven基础篇

主要内容

image-20220705145602357

1、Maven的简介

1.1 什么是Maven

Maven[ˈmeɪvn],这个词可以翻译为“专家、内行”,是一个项目管理工具,可以对 Java 项目进行自动化的构建和依赖管理。

它包含了:项目对象模型 (POM,Project Object Model),项目生命周期(Project Lifecycle),依赖管理系统(Dependency Management System)和各种插件。插件主要用来实现生命周期各个阶段(phase)的目标(goal)。Maven的组成如下所示:

img

1.2 传统开发的痛点

1、我们需要在工程中引用各种 jar 包,尤其是比较大的工程,引用的 jar 包往往有几十个乃至上百个, 每用到一种 jar 包,都需要手动引入到工程的lib目录,而且经常遇到各种让人抓狂的 jar 包冲突。

2、当新人接手一个项目时,就需要把项目中所有的依赖都下载到自己电脑上,非常麻烦,不同项目可能存在相同的依赖包,非常占电脑存储空间。

3、构建(build)是每个程序员每天都在做的事情,早上来到公司,第一件事就是从源码库中签出最新的代码,然后单元测试,如果测试失败,会找相关同事一起调试,修复代码。然后再回到自己的工作上来,编写自己的单元测试和产品代码。而Maven提供了只需要运行一条简单的命令,就能完成重复的,繁琐的构建和测试动作。

总结一下,我们会发现我们每天相当一部分时间花在了编译、运行单元测试、生成文档、打包和部署等繁琐且不起眼的工作上了,Maven能够是我们从这种繁琐的工作中解脱出来,只需要一条命令所有繁琐的步骤都能够自动完成。

1.3 项目构建工具的历史

Ant -> Maven -> Gradle

1、Ant

说起Ant,就不得不说另一个Apache开源项目Tomcat。Tomcat作为轻量级Web容器,早已声名鹊起。最开始的时候,Ant是Tomcat的一部分,Ant的唯一目的就是build Tomcat。

不久,很多Java开源项目意识到Ant的简洁适用,更重要的是弥补Makefiles的不足。自从Jakarta以及Apache项目开始采用Ant以来,作为构建工具的Ant很快发展在各种各样的项目中。

在2000年1月,Ant脱离了Tomcat,成为独立的Apache开源项目,由独立的CVS模块维护,正式更名为Apache Ant。

Ant实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" ?>  
<project name="HelloWorld" default="run" basedir=".">
<property name="src" value="src"/>
<property name="dest" value="classes"/>
<property name="jarfile" value="hello.jar"/>
<target name="init">
<mkdir dir="${dest}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${dest}"/>
</target>
<target name="build" depends="compile">
<jar jarfile="${jarfile}" basedir="${dest}"/>
</target>
<target name="test" depends="build">
<java classname="test.ant.HelloWorld" classpath="${hello_jar}"/>
</target>
<target name="clean">
<delete dir="${dest}" />
<delete file="${hello_jar}" />
</target>
</project>

由示例,得知Ant定义了五个任务,init, compile, build, test,clean。

每个任务做什么都定义清楚了。在打包之前要先编译,所以通过depends来指定依赖的路径。

如果在命令行里执行ant build,那就会先执行compile,而compile又依赖于init,所以就会先执行init。

1
ant test

通过命令就可以执行编程,打包,测试。为开发者带来了很大的便利,提供了工作效率。

但是Ant有一个很致命的缺陷,那就是没办法管理依赖。每次打包都需要手动将正确的依赖拷贝到lib目录中去,这个工作不仅枯燥还容易出错,为了解决这个问题Maven如约而至。

2、Maven

aven之前我们经常使用Ant来进行Java项目的构建,然后Ant仅是一个构建工具,它并未对项目的中的工程依赖以及项目本身进行管理,并且Ant作为构建工具未能消除软件构建的重复性,因为不同的项目需要编写对应的Ant任务。

Maven作为后来者,继承了Ant的项目构建功能,并且提供了依赖关系,项目管理的功能,因此它是一个项目管理和综合工具。

Maven 发展历程: Maven –> Maven2 –> Maven3

Maven示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<project ...xmlns...>
<groupId>net.devcheng.demo</groupId>
<artifactId>Example</artifactId>
<version>0.1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>

相对于Ant来说,Maven抛弃了Ant中通过target定义任务的做法,对于依赖引入了生命周期。

3、Gradle

Gradle是一个基于Apache Ant和ApacheMaven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,目前也增加了基于Kotlin语言的kotlin-based DSL,抛弃了基于XML的各种繁琐配置。

由以上定义得知,Gradle已经抛弃了Ant,Maven中Xml配置的形式。Gradle继承了Maven中仓库,坐标,依赖这些核心概念。文件的布局也和Maven相同。但同时,又继承了Ant中target的概念,我们又可以重新定义自己的任务(在Gradle中叫做task)。

Gradle示例:

1
2
3
4
5
6
7
8
9
10
apply plugin: 'java'

repositories {
jcenter()
}
dependencies {
compile 'org.slf4j:slf4j-api:1.7.21'
your tests.
testCompile 'junit:junit:4.12'
}

1.4 Maven的四大特性

1、依赖管理

Maven 为 java 世界引入了一个新的依赖管理系统:jar 包管理。当需要使用某个 jar 包时,不需要再从网络上下载并导入了,只需要配置其依赖即可;而当 jar 包需要升级时,也不需要重新下载最新的包并重新导入,只要修改 pom.xml 配置文件即可。在 java 世界中,可以用 groupId、artifactId、version 组成的 Coordination(坐标)唯一标识一个依赖。

  同时,任何基于 Maven 构建的项目自身也必须定义这三项属性,生成的包可以是 jar 包,也可以是 war 包。一个典型的依赖引用如下所示:

1
2
3
4
5
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>

2、多模块构建(聚合与继承)

项目复查时,使用将 dao、service、controller 层分离的方式,将一个项目分解为多个模块已经是一个很通用的方式。

  在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 <modules>标签来定义一组子模块。parent POM 不会有什么实际构建产出,而 parent POM 中的 build 配置以及依赖配置都会自动继承给子模块。

3、一致的项目结构

在 Ant 时代,大家创建 java 项目目录时比较随意,然后通过 Ant 配置指定哪些属于 source,哪些属于 test source等。并且,不同的 IDE (IDEA和Eclipse)创建项目时,目录结构是不一样的,这就会导致两种 IDE 的项目不能兼容导入。而 Maven 在设计之初的理念就是 “Conversion over configuration”(约定大于配置),其制定了一套统一的项目目录结构作为标准的 java maven 项目结构,解决了不同 IDE 带来的文件目录不一致问题,只要是 Maven 项目,在各个 IDE 中建立的项目结构都是一样的。

4、一致的构建模型和插件机制

  在编写 JavaWeb 项目时需要使用类似 tomcat 的服务器,我们可以通过插件的形式将服务器引入进来。如果需要使用 jetty 的服务器,也可以通过插件的形式将 jetty 服务器引入进来。

tomcat服务器插件:

1
2
3
4
5
6
7
8
9
10
11
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 项目访问路径 -->
<path>/demo</path>
<!-- 访问项目的端口号 -->
<port>8081</port>
</configuration>
</plugin>

jetty服务器插件:

1
2
3
4
5
6
7
8
9
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.25</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<contextPath>/test</contextPath>
</configuration>
</plugin>

2、Maven的安装配置和目录结构

2.1 安装配置

2.1.1 下载Maven

官网下载Maven:http://maven.apache.org/download.html

选择版本: apache-maven-3.8.3-bin.zip

2.1.2 配置环境变量

  1. 解压后,新建一个MAVEN_HOME系统变量,把Maven根目录配置到该系统变量中。

    image-20220627153422912

  2. 系统变量PATH中,新增一条%MAVEN_HOME%\bin

    image-20220627153542474

注:maven解压后存放的目录不要包含中文或空格。

  1. 测试是否配置成功

    win+R打开cmd窗口,键入mvn -v,弹出如下信息则是配置成功,若提示不是内部或外部命令,也不是可运行的程序 或批处理文件。则是环境变量配置存在问题。

    image-20220627154122635

  2. 配置字符编码为UTF-8

    默认字符编码为GBK

    新建一条系统环境变量MAVEN_OPTS值设置为-Xms256m -Xmx512m -Dfile.encoding=UTF-8

image-20220627153934743

重新打开一个命令行窗口,键入mvn -v,如果显示platform encoding:UTF-8,则是配置成功。

image-20220627154315548

2.1.3 配置远程仓库

打开maven目录中的conf/setting.xml文件,找到<mirrors>标签,将其中的默认镜像配置删除或注释,更改为阿里云的仓库地址。

1
2
3
4
5
6
7
8
<mirrors>		
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>

默认仓库源在国外,速度比较慢。

可参考https://developer.aliyun.com/mvn/guide来配置阿里云私服(镜像)

2.1.4 配置JDK版本

配置JDK版本为1.8

打开maven目录中的conf/setting.xml文件,在<profiles>标签中配置如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
 <profile>
<id>jdk-1.8</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>

2.2 Maven目录结构

maven统一了目录结构,不论是在eclipse还是idea,都能够成功导入并编译运行。

maven的项目结构如下表:

目录 功能
${basedir} 存放pom.xml和所有的子目录
${basedir}/src/main/java 存放项目中java源代码
${basedir}/src/main/resource 存放项目中的资源文件,比如db.properties等
${basedir}/src/test/java 存放项目中测试类的代码,比如JUnit代码
${basedir}/src/test/resource 存放测试使用的资源文件

2.2.1 手动创建以上目录并编译

  1. 选择一个位置创建目录名为maven-hello的项目。

maven1

  1. 创建完成目录结构如图。

    image-20220627155540537

  2. pom.xml文件添加内容,内容如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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>cn.yuencode</groupId>
    <artifactId>maven-hello</artifactId>
    <version>1.0-SNAPSHOT</version>
    </project>
  3. 编写主函数

1
2
3
4
5
6
package cn.yuencode.mavenhello;
public class Hello{
public static void main(String[] args) {
System.out.println("hello maven");
}
}

image-20220627160314651

  1. 编译

    命令行进入maven-hello根目录

    执行命令

    1
    mvn compile # 编译

    第一次执行需要下载依赖,需要一段时间。

    image-20220627161559800

  2. 运行

    执行命令

    1
    mvn exec:java -Dexec.mainClass="cn.yuencode.mavenhello.Hello"

    image-20220627161758305

3、IDEA集成maven环境

选择 “File” —> “New Projects Settings” —> “Settings for New Projects…” —> 搜索 “Maven

image-20220704144414786

选择本地Maven路径:

image-20220704144708728

配置settings.xml和本地仓库路径:

image-20220704144836471

4、Maven项目的创建

4.1 Java项目

4.1.1 新建项目

  1. 选择File->New->Project,选择Maven,选择版本为1.8的JDK
  2. 选择从骨架创建,并选择骨架org.apache.maven.archetypes:maven-archetype-quickstart

image-20220704145740702

  1. 定义项目名称、GroupId、ArtifactId、Version

    image-20220704150219139

  2. 配置Maven,如果与设置中的一致,点击Finish即可。

    image-20220704150321101

  3. 等待完成后,查看目录

    有时候目录并没有创建完整,这时候需要我们手动创建一下,如下图,资源目录没有被创建,我们需要手动创建一下资源目录

    image-20220704150607303

    注意,目录创建成功后需要标记为资源目录

    image-20220704150818444

    image-20220704150828061

4.1.2 编译项目

  1. 添加配置

    image-20220704151140849

  2. 创建一条compile命令

    image-20220704151257193

  3. 执行命令

    分为普通模式调试模式

    image-20220704151430754

  4. 编译成功

    image-20220704151529847

4.2 Web项目

4.2.1 新建项目

  1. 勾选从骨架创建,选择骨架org.apache.maven.archetypes:maven-archetype-webapp

    image-20220704151732016

  2. 定义项目名称等信息

    image-20220704151841485

  3. 项目目录结构

    image-20220704152007737

4.2.2 启动项目

  1. 修改pom.xml中jdk版本为1.8或其它对应版本

    1
    2
    3
    4
    5
    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
  2. 设置单元测试版本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <dependencies>
    <!-- Junit版本修改为4.12 -->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
    </dependency>
    </dependencies>
  3. 删除pluginManagement标签

    1
    2
    3
    4
    <!-- 将这个标签及标签中的内容全部删除 -->
    <pluginManagement>
    ...
    </pluginManagement>
  4. 添加web部署插件

    1
    2
    3
    4
    5
    6
    <build>
    <plugins>
    <plugin>
    </plugin>
    </plugins>
    </build>

    tomcat插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- Tomcat插件 -->
    <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.1</version>
    <configuration>
    <port>8081</port> <!-- 启动端口 默认:8080 -->
    <path>/maven02</path> <!-- 项目的站点名,即对外访问路径 -->
    <uriEncoding>UTF-8</uriEncoding> <!-- 字符集编码 默认:ISO-8859-1 -->
    <server>tomcat7</server> <!-- 服务器名称 -->
    </configuration>
    </plugin>

    jetty插件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!-- jetty插件 -->
    <plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>9.4.5.v20170502</version>
    <configuration>
    <httpConnector>
    <port>8000</port><!-- 设置启动的端口号 -->
    </httpConnector>
    <webApp> <!-- 可指定当前项目的站点名 -->
    <contextPath>/maven02</contextPath>
    </webApp> <!-- 热部署,每10秒扫描一次 -->
    <scanIntervalSeconds>10</scanIntervalSeconds>
    </configuration>
    </plugin>
  5. 配置中添加命令mvn tomcat7:runmvn jetty:run或直接运行命令。

    image-20220704153339804

image-20220704153150637

image-20220704153350516

image-20220704153129488

5、Maven命令

作为开发利器的maven,为我们提供了十分丰富的命令,了解maven的命令行操作并熟练运用常见的maven命令还是十分必要的,即使譬如IDEA等工具给我提供了图形界面化工具,但其底层还是依靠maven命令来驱动的。

Maven的命令格式如下:

1
mvn [plugin-name]:[goal-name]

命令代表的含义:执行 plugin-name 插件的goal-name目标

4.1 常用命令

命令 描述
mvn -version 显示maven版本信息
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 eclipse:eclipse 将项目转换为Eclipse项目
mvn dependency:tree 打印出项目的整个依赖树
mvn archetype:generate 创建Maven的普通java项目
mvn tomcat7:run 在tomcat容器中运行web应用
mvn jetty:run 调用jetty插件的run目标在jetty servlet容器中启动web应用

注意:运行maven命令时,需要定位到maven项目的目录,即项目的pom.xml文件所在的目录,否则需要通过参数指定项目的目录。但有些命令则不需要比如mvn archetype:generate

5.2 命令参数

maven很多命令都可以携带参数以执行更精确的任务。

5.2.1 -D 传入属性参数

mvn package -Dmaven.test.skip=true

-D开头传入属性参数,将maven.test.skip的值设置为true,即maven在打包时会跳过单元测试。同理,mvn deploy -Dmaven.test.skip=true表示部署项目跳过但单元测试。

5.2.2 -P 使用指定的Profile配置

一般在项目开发中需要有多个环境,比如开发、测试、预发、正式4个环境。

我们可以通过配置pom.xml,在不同环境中使用不同的配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<profiles>
<profile>
<id>dev</id>
<properties>
<env>dev</env>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>pre</id>
<properties>
<env>pre</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
</profiles> ...... <build>
<filters>
<filter>config/${env}.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
......
</build>

具体操作步骤见6.2 多环境打包

6、Maven的打包操作

对于企业级项目,无论是进行本地测试,还是测试环境测试以及最终的项目上线,都会涉及到项目的打包操作。对于不同环境下项目的打包,对应项目的配置文件都会有所不同,实现打包的方式有很多种,可以通过ant、或者idea自带的打包功能实现项目打包,但当项目很大并且需要的外界配置很多时,此时打包的配置就会异常复杂,对于Maven项目,我们可以通过pom.xml配置的方式实现打包时环境的选择,相比较其他形式打包工具,通过Maven只需要简单的配置,就可以轻松完成不同环境下项目的整体打包。

6.1 补全目录结构

使用idea创建项目,目录结构可能会缺失,这时需要我们手动添加对应的目录。

image-20220704165343949

补全目录后项目结构

image-20220704165506651

6.2 多环境打包

6.2.1 添加不同环境的配置文件

image-20220704165922498

内容分别为

  • dev(开发环境)

    bean.xml

    1
    2
    3
    <bean>
    dev
    </bean>

    db.properties

    1
    2
    jdbc.username=root
    jdbc.password=123456
  • test(测试环境)

    bean.xml

    1
    2
    3
    <bean>
    test
    </bean>

    db.properties

    1
    2
    jdbc.username=test123
    jdbc.password=666666
  • prod(product生成环境)

    bean.xml

    1
    2
    3
    <bean>
    prod
    </bean>

    db.properties

    1
    2
    jdbc.username=prod
    jdbc.password=daghi9aeghawgaej9

6.2.2 pom.xml中添加profile配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 打包环境配置 开发环境 测试环境 正式环境 -->
<profiles>
<profile>
<id>dev</id>
<properties>
<env>dev</env>
</properties> <!-- 未指定环境时,默认打包dev环境 -->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
<properties>
<env>test</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
</profiles>

6.2.3 pom.xml中设置资源文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<build>
<!-- 对于项目资源文件的配置放在build中 -->
<resources>
<resource>
<directory>src/main/resources/${env}</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.tld</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

6.2.4 执行打包操作

不同环境使用命令进行指定

  • 开发环境(默认为开发环境)

    1
    clean compile package -Dmaven.test.skip=true

    image-20220704171523496

  • 测试环境

    1
    clean compile package -Ptest -Dmaven.test.skip=true

    image-20220704171621541

  • 生产环境

    1
    clean compile package -Pprod -Dmaven.test.skip=true

    image-20220704171712521

6.2.5 查看打包文件

打包成功后,我们可以在target文件夹中看到编译成功的项目,其中我们可以看到到资源文件的内容。

比如,在使用clean compile package -Pprod -Dmaven.test.skip=true打包成生产环境后,我们可以看到资源文件内容如下:

image-20220704172145512

6.2.6 原理

我们命令通过-P指定参数,指定的参数会去找pom.xml中的profile标签的id,如果有对应的则使用该profile,而<directory>src/main/resources/${env}</directory>中的${env}则使用profile标签中env标签中的内容。而我们在resources资源文件夹的目录名与env标签中是一致的。

7、Maven仓库基本概念

当第一次运行Maven命令的时候, 你需要Internet链接, 因为它需要从网上下载一些文件。 那么它从哪里下载呢? 它是从Maven默认的远程库下载的。 这个远程仓库有Maven的核心插件和可供下载的jar文件。

对于Maven来说, 仓库只分为两类: 本地仓库和远程仓库

当Maven根据坐标寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在,则直接使用; 如果本地没有,Maven就会去远程仓库查找,发现需要的构件之后,下载到本地仓库再使用。 如果本地仓库和远程仓库都没有,Maven就会报错。

远程仓库分为三种: 中央仓库,私服, 其他公共库

中央仓库是在默认配置下,Maven下载jar包的地方。

私服是另一种特殊的远程仓库,为了节省带宽和时间,应该在局域网内架设一个私有的仓库服务器,用其代理所有外部的远程仓库。 内部的项目还能部署到私服上供其他项目使用。

一般来说,在Maven项目目录下,没有诸如lib/这样用来存放依赖文件的目录。 当Maven在执行编译或测试时,如果需要使用依赖文件,它总是基于坐标使用本地仓库的依赖文件。

默认情况下, 每个用户在自己的用户目录下都有一个路径名为.m2/repository/的仓库目录。 有时候,因为某些原因(比如c盘空间不足),需要修改本地仓库目录地址。

对于本地仓库存储路径的修改,可以通过maven 配置文件conf 目录下settings.xml来指定仓库路径。

1
2
3
4
<!-- 设置到指定目录中,路径的斜杆不要写反 -->
<settings>
<localRepository>D:/maven/repository</localRepository>
</settings>

7.1 中央仓库

由于原始的本地仓库是空的,maven必须知道至少一个可用的远程仓库,才能执行maven命令的时候下载到需要的构件。中央仓库就是默认的远程仓库。

maven-model-builder-3.3.9.jar maven自动的 jar 中包含了一个 超级POM。定义了默认中央仓库的位置。

中央仓库包含了2000多个开源项目,接收每天1亿次以上的访问,由于访问量大,速度相较慢了。我们一般会更改远程仓库的路径。

7.2 私服

私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务, 私服代理广域网上的远程仓库,供局域网内的maven用户使用。 当maven需要下载构件时, 它去私服当中找,如果私服没有, 则从外部远程仓库下载,并缓存在私服上, 再为maven提供。

此外,一些无法从外部仓库下载的构件也能从本地上传到私服提供局域网中其他人使用。

配置方式:在项目pom.xml 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>public</id>
<name>Public Repositories</name>
<url>http://192.168.0.96:8081/content/groups/public/</url>
</repository>
<repository>
<id>getui-nexus</id>
<url>http://mvn.gt.igexin.com/nexus/content/repositories/releases/</url>
</repository>
</repositories>

公司内部应该建立私服:

  • 节省自己的外网带宽

  • 加速maven构建

  • 部署第三方控件

  • 提高稳定性

  • 降低中央仓库的负荷

7.3 其他公共库

常用的阿里云仓库配置:阿里云maven仓库 https://developer.aliyun.com/mvn/guide

1
2
3
4
5
6
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

8、Maven构建多模块

以下案例以四个模块来创建项目。

  • 模块maven-parent 父模块(pom)
  • 模块maven-dao 数据访问层(jar)
  • 模块maven-service 项目的业务逻辑层(jar)
  • 模块maven-controller 控制器层(war)

依赖关系:

A2EBDFD9F0FDB047D8D6D349C59C8E3B

8.1 创建空项目

选择File->New->Project->Empty Project

image-20220704214743066

8.2 创建maven-parent模块

不勾选Create from archetype

image-20220704215638599

设置GroupId和ArtifactId、模块名称等

image-20220704215800635

可以删除除了pom.xml的其它文件,仅保留pom.xml文件即可。

image-20220704215929466

8.3 创建maven-dao模块

maven-parent目录点击右键选择New->Module

image-20220704220505151

输入模块名称

image-20220704220636765

8.4 创建另外两个模块

同样的步骤创建另外两个模块,创建完成后目录结构如下:

image-20220704221030148

8.5 配置模块之间的依赖关系

maven-parent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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>cn.yuencode</groupId>
<artifactId>maven-parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>maven-dao</module>
<module>maven-service</module>
<module>maven-controller</module>
</modules>
</project>

maven-dao

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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">
<parent>
<artifactId>maven-parent</artifactId>
<groupId>cn.yuencode</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>maven-dao</artifactId>
</project>

maven-service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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">
<parent>
<artifactId>maven-parent</artifactId>
<groupId>cn.yuencode</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>maven-service</artifactId>

<dependencies>
<dependency>
<groupId>cn.yuencode</groupId>
<artifactId>maven-dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

maven-controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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">
<parent>
<artifactId>maven-parent</artifactId>
<groupId>cn.yuencode</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>maven-controller</artifactId>

<dependencies>
<dependency>
<groupId>cn.yuencode</groupId>
<artifactId>maven-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

8.6 添加方法和依赖

maven-dao添加类

1
2
3
4
5
6
7
package cn.yuencode;

public class UserDao {
public void toLogin(){
System.out.println("maven-dao UserDao toLogin");
}
}

maven-service添加类

1
2
3
4
5
6
7
8
9
10
package cn.yuencode;

public class UserService {
private UserDao userDao = new UserDao();

public void testService(){
userDao.toLogin();
System.out.println("maven-service UserService testService");
}
}

maven-controller添加类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.yuencode;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/user")
public class UserController extends HttpServlet {
UserService userService = new UserService();
public void login(){
userService.testService();
System.out.println("maven-controller UserController login");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
login();
resp.getWriter().write("success");
}
}

maven-parent父模块中依赖管理中添加servlet依赖

1
2
3
4
5
6
7
8
9
10
11
<dependencyManagement>
<dependencies>
<!-- Servlet的依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>

maven-controller添加servlet依赖

不用指定版本,版本在父工程maven-parent中统一管理。

1
2
3
4
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>

8.7 启动项目

补充:

  1. maven-controller模块中配置打包方式为war.

    pom.xml

    1
    <packaging>war</packaging>
  2. maven-controller模块中手动创建webapp文件夹

    image-20220704230158879

8.7.1 方式一:在maven-controller中添加tomcat7插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<build>
<!-- 添加插件 -->
<plugins> <!-- tomcat7插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8088</port>
<path>/maven-multi</path>
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
</build>

依次在maven-parentmaven-daomaven-service中执行mvn install

最后在maven-controller中执行mvn tomcat7:run

8.7.2 方式二:在父工程maven-parent中添加tomcat7插件

此方式的好处是不需要依次在各个依赖模块中手动执行mvn install命令。

直接在父工程maven-parent中执行mvn tomcat7:run即可。

访问地址:http://localhost:8088/maven-multi

运行结果:

image-20220704225637025

image-20220704225707216

完整代码:maven-multi.zip

9、Maven依赖的基本概念

9.1 依赖的基本配置

根元素project下的dependencies可以包含多个 dependency元素,以声明多个依赖。每个依赖都应该包含以下元素:

  1. groupIdartifactIdversion:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。

  2. type:依赖的类型,大部分情况下不需要声明。默认值为jar

  3. scope:依赖的范围。包含有compiletestprovidedruntimesystem

    • compile:编译依赖范围。

      如果没有指定,默认使用该范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath有效。

    • test:测试依赖范围。

      使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目时将无法使用该依赖。比如,Junit,它只有在编译测试代码以及运行测试的时候才需要被使用到。

    • provided:已提供依赖范围。

      使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。例如servlet-api,编译和测试项目的时候需要该依赖,但是运行项目的时候,由于tomcat容器以及提供,就不需要Maven在重复引入。

    • runtime:运行时依赖范围。

      使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。例如JDBC驱动,项目主代码的编译只需要JDK提供的JDBC接口,不需要实现类,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。

    • system:系统依赖范围。

      该依赖范围与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围依赖时必须通过systemPath标签显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。

引入外部依赖

如果我们需要引入第三方库文件到项目,该怎么操作呢?

要添加依赖项,我们一般是先在 src 文件夹下添加 lib 文件夹,然后将你工程需要的 jar 文件复制到 lib 文件夹下。我们使用的是 ldapjdk.jar ,它是为 LDAP 操作的一个帮助库:

image-20220705135112134

然后添加以下依赖到 pom.xml 文件中:

1
2
3
4
5
6
7
8
9
10
<dependencies>
<!-- 在这里添加你的依赖 -->
<dependency>
<groupId>ldapjdk</groupId> <!-- 库名称,也可以自定义 -->
<artifactId>ldapjdk</artifactId> <!--库名称,也可以自定义-->
<version>1.0</version> <!--版本号-->
<scope>system</scope> <!--作用域-->
<systemPath>${basedir}\src\lib\ldapjdk.jar</systemPath> <!--项目根目录下的lib文件夹下-->
</dependency>
</dependencies>
  1. optional:标记依赖是否可选。

  2. exclusions:用来排除传递依赖。

9.2 依赖范围

首先需要知道,Maven在编译项目主代码的时候需要使用一套classpath。比如:编译项目代码的时候需要用到spring-core,该文件以依赖的方式被引入到classpath中。其次,Maven在执行测试的时候会使用另外一套classpath。如:junit。

最后在实际运行项目时,又会使用一套classpath,spring-core需要在该classpath中,而junit不需要。

那么依赖范围就是用来控制与这三种classpath(编译classpath、测试classpath、运行时classpath)的关系,Maven有以下几种依赖范围:

  • compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖的Maven依赖,对于编译、测试、运行都有效。
  • test:测试依赖范围。只有在测试的时候才有效。比如junit。
  • provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试有效,但在运行时无效,例如servlet-api
  • runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行有效,但在编译代码时无效,例如JDBC驱动。
  • system:系统依赖范围。与provided的作用域一致,一般不使用。
依赖范围 作用域 例子
compile 编译+测试+运行 spring-core
test 测试 junit
provided 编译+测试 servlet-api
runtime 运行+测试 jdbc驱动
system 编译+测试 本地的依赖,maven仓库之外的依赖类库

可以发现5个依赖范围都包含测试作用域。provided为编译+测试,runtime为运行+测试,test为测试。其实可以理解,测试环境为本地环境其中类方法函数只能通过依赖提供,所以这个是必选的。

9.3 传递依赖

传递依赖机制,让我们在使用某个jar的时候就不用去考虑它依赖了什么,也不同担心引入多余的依赖。Maven会解析各个直接依赖的pom,将那些必要的间接依赖,以传递性依赖的形式引入到当前项目中。

注意:传递依赖有可能产生冲突。

冲突场景:

1
2
3
4
B依赖2.0版本的C
A依赖了D,而D依赖了1.0版本的C
B --> C(2.0)
A --> D --> C(1.0)

这时候存在两个不同版本的C,冲突!需要我们手动进行解决,使用exclusion标签进行排除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>A</groupId>
<artifactId>A</artifactId>
<version>xxx</version>
<exclusions>
<exclusion>
<groupId>C</groupId>
<artifactId>C</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>B</groupId>
<artifactId>B</artifactId>
</dependency>
</dependencies>