Maven

YangeIT大约 28 分钟Maven但愿测试Maven依赖传递

Maven

今日目标

目标

  • 能够知道Maven的作用 🍐❤️
  • 能够安装Maven并且在Idea中配置Maven ❤️ ✏️
  • 能够创建Maven和导入Maven工程 ❤️ ✏️
  • 能够知道Maven本地仓库、远程仓库、中央仓库的作用🍐
  • 能够知悉Maven常见指令的效果 ✏️
  • 能够理解Maven的依赖配置 🍐
  • 能够知道单元测试的作用🍐
  • 能够认识单元测试常见注解 ✏️
  • 能够理解的断言的作用🍐

知识储

  1. 理解在开发过程中要导入其他人开发的java代码,提高开发效率
  2. 能够理解开发一个功能,就要测试一个功能的重要性,一步一个脚印!
  3. 知悉javac的作用

1. Maven初识 🍐

Maven初识

1.什么是Maven?

image-20231121170658821

Maven 是一款用于管理和构建Java项目的工具,是Apache旗下的一个开源项目 。

Apache 软件基金会,成立于1999年7月,是目前世界上最大的最受欢迎的开源软件基金会,也是一个专门为支持开源项目而生的非盈利性组织。

开源项目:https://www.apache.org/index.html#projects-listopen in new window

那我们之前在JavaSE阶段,没有使用Maven,依然可以构建Java项目。 我们为什么现在还要学习Maven呢 ? 那接下来,我们就来聊聊Maven的作用。

2.Maven的作用

image-20231121171743838

作用详解

  1. 依赖管理
  2. 项目构建
  3. 统一项目结构

依赖管理

方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题。

1). 使用maven前

我们项目中要想使用某一个jar包,就需要把这个jar包从官方网站下载下来,然后再导入到项目中。然后在这个项目中,就可以使用这个jar包了。

image-20231121172020180

2). 使用maven后

当使用maven进行项目依赖(jar包)管理,则很方便的可以解决这个问题。 我们只需要在maven项目的pom.xml文件中,添加一段如下图所示的配置即可实现。

image-20231121172109208

在maven项目的配置文件中,加入上面这么一段配置信息之后,maven会自动的根据配置信息的描述,去下载对应的依赖。 然后在项目中,就可以直接使用了。

总结

课堂作业

  1. Maven的作用有哪些?或者说为什么要使用Maven? 🎤

2. Maven概述和安装 🍐 ✏️

Maven概述

Maven介绍

Apache Maven是一个项目管理和构建工具,它基于项目对象模型(Project Object Model , 简称: POM)的概念,通过一小段描述信息来管理项目的构建、报告和文档。

官网:https://maven.apache.org/open in new window

Maven的作用:

  1. 方便的依赖管理
  2. 统一的项目结构
  3. 标准的项目构建流程

总结

课堂作业

  1. 访问中央仓库:https://repo1.maven.org/maven2/open in new window 观察里面有什么?
  2. 参考上述安装步骤,安装Maven客户端到本地安装路径非中文目录🎤

3. IDEA集成Maven ✏️

3.1 配置Maven和创建Maven工程

我们要想在IDEA中使用Maven进行项目构建,就需要在IDEA中集成Maven

IDEA集成Maven

  1. 配置Maven环境

    • 全局设置
  2. 创建Maven工程

配置Maven环境操作

全局设置

1、进入到IDEA欢迎页面 点击查看Gif动图open in new window

  • 选择 IDEA中 File => close project

2、打开 All settings , 选择 Build,Execution,Deployment => Build Tools => Maven

image-20231121181406774

3、配置工程的编译版本为 你电脑安装的jdk版本

image

这里所设置的maven的环境信息,并未指定任何一个project,此时设置的信息就属于全局配置信息。 以后,我们再创建project,默认就是使用我们全局配置的信息。

总结

课堂作业

  1. Idea全局设置Maven,到底是设置什么?怎么判断设置Maven正确? 🎤
  2. 根据创建Maven项目的提示,独自创建一个maven-project1的maven工程,创建后观察工程目录结构是否和案例的一致? 🎤

3.2 Maven工程详解

Maven工程详解

POM配置详解

POM (Project Object Model) :指的是项目对象模型,用来描述当前的maven项目。

  • 使用pom.xml文件来实现

pom.xml文件:

<?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">
    <!-- POM模型版本 -->
    <modelVersion>4.0.0</modelVersion>

    <!-- 当前项目坐标 -->
    <groupId>com.itheima</groupId>
    <artifactId>maven_project1</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <!-- 打包方式 -->
    <packaging>jar</packaging>
 
</project>

pom文件详解:

  • <project> :pom文件的根标签,表示当前maven项目
  • <modelVersion> :声明项目描述遵循哪一个POM模型版本
    • 虽然模型本身的版本很少改变,但它仍然是必不可少的。目前POM模型版本是4.0.0
  • 坐标 :<groupId><artifactId><version>
    • 定位项目在本地仓库中的位置,由以上三个标签组成一个坐标
  • <packaging> :maven项目的打包方式,通常设置为jar或war(默认值:jar)
  1. jar是java普通项目打包,通常是开发时要引用通用类,打成jar包便于存放管理,
  2. war是java web项目打包,web网站完成后,打成war包部署到服务器,目的是为了节省资源,提供效率

导入Maven项目操作

方式1

打开IDEA,选择 File -> Project Structure -> Modules -> Import Module -> 选择maven项目的pom.xml

image-20231121182520581

说明:如果没有Maven面板,选择 View => Appearance => Tool Window Bars

image-20220616111937679
image-20220616111937679

总结

课堂作业

  1. Maven坐标有有什么特点?有合作用?主要组成部分是哪些?🎤
  2. 解读一下:下面的Maven坐标,每一行什么意思?
<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-all</artifactId>
   <version>5.7.16</version>
</dependency>
  1. 将上面的坐标复制 ,粘贴到你的项目的pom文件中,刷新会发送什么?

4. 依赖管理 🍐

4.1 依赖配置 🍐✏️

依赖配置

依赖:指当前项目运行所需要的jar包。一个项目中可以引入多个依赖:

例如:在当前工程中,我们需要用到logback来记录日志,此时就可以在maven工程的pom.xml文件中,引入logback的依赖。具体步骤如下:

  1. 在pom.xml中编写<dependencies>标签
  2. <dependencies>标签中使用<dependency>引入坐标
  3. 定义坐标的 groupId、artifactId、version
<dependencies>
    <!-- 依赖 : commons-io -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>
  1. 点击刷新 按钮,引入最新加入的坐标

    刷新依赖:保证每一次引入新的依赖,或者修改现有的依赖配置,都可以加入最新的坐标

image-20231121182814533

注意事项:

  1. 如果引入的依赖,在本地仓库中不存在,将会连接远程仓库 / 中央仓库,然后下载依赖(这个过程会比较耗时,耐心等待)国内镜像仓库,会快很多
  2. 如果不知道依赖的坐标信息,可以到mvn的中央仓库(https://mvnrepository.com/)中搜索,或者直接在百度进行查询open in new window

添加依赖的几种操作方式:

  1. 利用中央仓库搜索的依赖坐标少用

总结

课堂作业

  1. 添加依赖的几种操作方式中,最常用的是哪种?🎤
  2. 添加单元测试junit所需要的依赖,可以百度查询!!✏️

4.2 依赖传递🍐

依赖传递

依赖具有传递性

早期我们没有使用maven时,向项目中添加依赖的jar包,需要把所有的jar包都复制到项目工程下。如下图所示,需要logback-classic时,由于logback-classic又依赖了logback-core和slf4j,所以必须把这3个jar包全部复制到项目工程下

image-20221201120514644
image-20221201120514644

我们现在使用了maven,当项目中需要使用logback-classic时,只需要在pom.xml配置文件中,添加logback-classic的依赖坐标即可。

image-20221201113659400
image-20221201113659400

在pom.xml文件中只添加了logback-classic依赖,但由于maven的依赖具有传递性,所以会自动把所依赖的其他jar包也一起导入。

依赖传递可以分为:

  1. 直接依赖 :在当前项目中通过依赖配置建立的依赖关系
  2. 间接依赖 :被依赖的资源如果依赖其他资源,当前项目间接依赖其他资源

image-20231121183842374

比如以上图中:

  • projectA依赖了projectB。对于projectA 来说,projectB 就是直接依赖 。
  • 而projectB依赖了projectC及其他jar包。 那么此时,在projectA中也会将projectC的依赖传递下来 。对于projectA 来说,projectC就是间接依赖。
image-20221201115801806
image-20221201115801806

总结

课堂作业

  1. A依赖于B,B依赖与C,请问A依赖C吗?谈谈原因?
  2. A依赖F,A依赖B,B依赖E,E依赖F,请问会出错吗?microphone:
  3. A依赖B,B依赖E,A依赖E吗?如果不想A依赖E,怎么做?microphone:

4.3 生命周期 🍐 ✏️

生命周期

介绍

  1. Maven的生命周期就是为了对所有的构建过程进行抽象和统一。 描述了一次项目构建,经历哪些阶段。

在Maven出现之前,项目构建的生命周期就已经存在,软件开发人员每天都在对项目进行清理,编译,测试及部署。虽然大家都在不停地做构建工作,但公司和公司间、项目和项目间,往往使用不同的方式做类似的工作。

Maven从大量项目和构建工具中学习和反思,然后总结了一套高度完美的,易扩展的项目构建生命周期。这个生命周期包含了项目的清理,初始化,编译,测试,打包,集成测试,验证,部署和站点生成等几乎所有构建步骤。

  1. Maven对项目构建的生命周期划分为3套(相互独立):
  • clean 清理工作。

  • default :核心工作。如:编译、测试、打包、安装、部署等。

  • site :生成报告、发布站点等。

4. 三套生命周期又包含哪些具体的阶段呢, 我们来看下面这幅图:

image-20220616124348972

我们看到这三套生命周期,里面有很多很多的阶段,这么多生命周期阶段,其实我们常用的并不多,主要关注以下几个:

clean :移除上一次构建生成的文件

compile :编译项目源代码

test :使用合适的单元测试框架运行测试(junit)

package :将编译后的文件打包,如:jar、war等

install :安装项目到本地仓库

  1. Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际工作。在Maven的设计中,实际任务(如源代码编译)都交由插件来完成。
image-20221130142100703

IDEA工具为了方便程序员使用maven生命周期,在右侧的maven工具栏中,已给出快速访问通道

image-20221201151340340
image-20221201151340340
  1. 生命周期的顺序是:clean --> validate --> compile --> test --> package --> verify --> install --> site --> deploy

我们需要关注的就是:clean --> compile --> test --> package --> install

说明:在同一套生命周期中,我们在执行后面的生命周期时,前面的生命周期都会执行。

思考:当运行package生命周期时,clean、compile生命周期会不会运行?

​ clean不会运行,compile会运行。 因为compile与package属于同一套生命周期,而clean与package不属于同一套生命周期。

操作

生命周期执行

在日常开发中,当我们要执行指定的生命周期时,有两种执行方式:

  1. 在idea工具右侧的maven工具栏中,选择对应的生命周期,双击执行
  2. 在DOS命令行中,通过maven命令执行

方式一:在idea中执行生命周期

  • 选择对应的生命周期,双击执行
image-20221201161957301

compile:

image-20221201163711835

test:

image-20221201164627403

package:

image-20221201165801341

总结

课堂作业

  1. 在已有的Maven工程,分别执行生命周期命令!!,观察效果,并完成下述选择题。

5. 测试

5.1 单元测试

单元测试

测试介绍

  • 测试: 是一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程。

  • 阶段划分: 单元测试、集成测试、系统测试、验收测试。

image-20231121205358842

代码操作

1). 单元测试

介绍:对软件的基本组成单位进行测试,最小测试单位。

目的:检验软件基本组成单位的正确性。

测试人员:开发人员


测试方法

  • 测试方法: 白盒测试、黑盒测试 及 灰盒测试。
image-20231121210849299

1). 白盒测试

清楚软件内部结构、代码逻辑。

用于验证代码、逻辑正确性。

总结

课堂作业

  1. 单元测试有何意义?🎤
  2. 打开已经开发的软件,点点点测试,是白还是黑盒测试?

5.2 Junit快速入门

Junit快速入门

单元测试

  • 单元测试:就是针对最小的功能单元(方法),编写测试代码对其正确性进行测试。

  • JUnit:最流行的Java测试框架之一,提供了一些功能,方便程序进行单元测试(第三方公司提供)。

在之前的课程中,我们进行程序的测试 ,都是main方法中进行测试 。如下图所示:

image-20231121212344174

通过main方法是可以进行测试的,可以测试程序是否正常运行。但是main方法进行测试时,会存在如下问题:

  1. 测试代码与源代码未分开,难维护。
  2. 一个方法测试失败,影响后面方法。
  3. 无法自动化测试,得到测试报告。

而如果我们使用了JUnit单元测试框架进行测试,将会有以下优势:

  1. 测试代码与源代码分开,便于维护。
  2. 可根据需要进行自动化测试。
  3. 可自动分析测试结果,产出测试报告。

image-20231121213030314

代码操作

入门程序

需求:使用JUnit,对UserService中的业务方法进行单元测试,测试其正确性。

  1. pom.xml中,引入JUnit的依赖。

    <!--Junit单元测试依赖-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.9.1</version>
        <scope>test</scope>
    </dependency>
    
  2. 在test/java目录下,创建测试类,并编写对应的测试方法,并在方法上声明@Test注解。

    @Test
    public void testGetAge(){
        Integer age = new UserService().getAge("110002200505091218");
        System.out.println(age);
    }
    
  3. 运行单元测试 (测试通过:绿色;测试失败:红色)。

    测试通过显示绿色

image-20231121213825685

​ 测试失败显示红色

image-20231121214044713

注意:

  1. 测试类的命名规范为:XxxxTest
  2. 测试方法的命名规范为:public void xxx()

总结

课堂作业

  1. junit测试需要添加什么注解?🎤

5.3 常见注解 🚀

常见注解

在JUnit中还提供了一些注解,还增强其功能,常见的注解有以下几个:

注解说明备注
@Test测试类中的方法用它修饰才能成为测试方法,才能启动执行单元测试
@BeforeEach用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。初始化资源(准备工作)
@AfterEach用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。释放资源(清理工作)
@BeforeAll用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。初始化资源(准备工作)
@AfterAll用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次。释放资源(清理工作)
@ParameterizedTest参数化测试的注解 (可以让单个测试运行多次,每次运行时仅参数不同)用了该注解,就不需要@Test注解了
@ValueSource参数化测试的参数来源,赋予测试方法参数与参数化测试注解配合使用
@DisplayName指定测试类、测试方法显示的名称 (默认为类名、方法名)

代码操作

演示 @BeforeEach@AfterEach@BeforeAll@AfterAll 注解:

public class UserServiceTest {

    @BeforeEach
    public void testBefore(){
        System.out.println("before...");
    }

    @AfterEach
    public void testAfter(){
        System.out.println("after...");
    }

    @BeforeAll //该方法必须被static修饰
    public static void testBeforeAll(){ 
        System.out.println("before all ...");
    }

    @AfterAll //该方法必须被static修饰
    public static void testAfterAll(){
        System.out.println("after all...");
    }

    @Test
    public void testGetAge(){
        Integer age = new UserService().getAge("110002200505091218");
        System.out.println(age);
    }
    
    @Test
    public void testGetGender(){
        String gender = new UserService().getGender("612429198904201611");
        System.out.println(gender);
    }
 }   

输出结果如下:

image-20231122100220525
image-20231122100220525

总结

课堂作业

  1. @BeforeEach注解有什么作用?🎤

5.4 断言 🍐

断言

JUnit提供了一些辅助方法,用来帮我们确定被测试的方法是否按照预期的效果正常工作,这种方式称为断言

断言方法描述
assertEquals(Object exp, Object act, String msg)检查两个值是否相等,不相等就报错。
assertNotEquals(Object unexp, Object act, String msg)检查两个值是否不相等,相等就报错。
assertNull(Object act, String msg)检查对象是否为null,不为null,就报错。
assertNotNull(Object act, String msg)检查对象是否不为null,为null,就报错。
assertTrue(boolean condition, String msg)检查条件是否为true,不为true,就报错。
assertFalse(boolean condition, String msg)检查条件是否为false,不为false,就报错。
assertSame(Object exp, Object act, String msg)检查两个对象引用是否相等,不相等,就报错。

代码操作

示例演示:

package com.itheima;

import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

@DisplayName("测试-学生业务操作")
public class UserServiceTest {
    @DisplayName("测试-获取年龄2")
    @Test
    public void testGetAge2(){
        Integer age = new UserService().getAge("110002200505091218");
        Assertions.assertNotEquals(18, age, "两个值相等");
//        String s1 = new String("Hello");
//        String s2 = "Hello";
//        Assertions.assertSame(s1, s2, "不是同一个对象引用");
    }

    @DisplayName("测试-获取性别2")
    @Test
    public void testGetGender2(){
        String gender = new UserService().getGender("612429198904201611");
        Assertions.assertEquals("男", gender);
    }

    @DisplayName("测试-获取性别3")
    @ParameterizedTest
    @ValueSource(strings = {"612429198904201611","612429198904201631","612429198904201626"})
    public void testGetGender3(String idcard){
        String gender = new UserService().getGender(idcard);
        Assertions.assertEquals("男", gender);
    }
}

测试结果输出:

image-20231122101105549
image-20231122101105549

5.5 依赖范围 🍐

依赖范围

依赖的jar包,默认情况下,可以在任何地方使用。

image-20231122101238601

在maven中,如果希望限制依赖的使用范围,可以通过 <scope>…</scope> 设置其作用范围。

image-20231121224153187

操作演示

作用范围

  • 主程序范围有效。(main文件夹范围内)
  • 测试程序范围有效。(test文件夹范围内)
  • 是否参与打包运行。(package指令范围内)

可以在pom.xml中配置 <scope></scope> 属性来控制依赖范围。

image-20231121224241781

如果对Junit单元测试的依赖,设置了scope为 test,就代表,该依赖,只是在测试程序中可以使用,在主程序中是无法使用的。所以我们会看到如下现象:

image-20231122101613107
image-20231122101613107

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

总结

scope的取值常见的如下:

scope主程序测试程序打包(运行)范例
compile(默认)YYYlog4j
test-Y-junit
providedYY-servlet-api
runtime-YYjdbc驱动

课堂作业

  1. 为什么要有scope不同值的设置?为什么不全部设置为compile?🎤

6. Maven常见问题 🍐

Maven常见问题

image-20231122101744998
  • 问题现象:Maven项目中添加的依赖,未正确下载,造成右侧Maven面板中的依赖报红,再次reload重新加载也不会再下载。

  • 产生原因:由于网络原因,依赖没有下载完整导致的,在maven仓库中生成了xxx.lastUpdated文件,该文件不删除,不会再重新下载。

image-20231122101913739

  • 解决方案:
    • 根据maven依赖的坐标,找到仓库中对应的 xxx.lastUpdated 文件,删除,删除之后重新加载项目即可。
    • 通过命令 (del /s *.lastUpdated) 批量递归删除指定目录下的 xxx.lastUpdated 文件,删除之后重新加载项目即可。
    • 重新加载依赖,依赖下载了之后,maven面板可能还会报红,此时可以关闭IDEA,重新打开IDEA加载此项目即可。