前言
注意本篇是需要一定的maven基础的 如果没有请移步Maven教程–上
手动创建Maven 项目- 理解Maven 底层机制
需求说明/图解
用手工的方式,创建maven 项目, 深刻理解Maven 工作机制
完成功能-说明
- 编写一个类, 有一个方法sum,可以返回两个数的和
- 编写测试类TestSum, 可以测试sum 是否正确.
- 使用指令完成Maven 的编译(complile)、测试(test)、打包(package)、安装(install)、清理(clean)等操作
手写方式
完成代码编写
- 先根据maven 项目的结构要求,在d 盘: 创建项目结构(如图)
- 创建\Hello.java ,说明: 这个文件可以手写, 也可以从快速入门项目拷贝一份.
public class Hello { public String sum(int n1, int n2) { System.out.println("sum= " + (n1 + n2)); return "sum= " + (n1 + n2); } }
- 创建HelloTest.java,说明: 这个文件可以手写, 也可以从快速入门项目拷贝一份, 同时注意这个文件是在src\test… 下的…
public class HelloTest {
@Test public void testSum() { Hello hello = new Hello(); String res = hello.sum(1, 9); //使用断言 /** * 1.Assert 是一个断言类,(就是判断实际返回的值和期望值是否相同) * 2.如果res 和 "sum=10" 是相同的,则通过断言 * 3.如果res 和 "sum=10" 是不相同的,则给出提示 * */ Assert.assertEquals("sum=10", res); }
- 创建pom.xml, 从快速入门项目拷贝一份即可. 放在和src 同级目录即可, 提示:使用记事本来保存, 保证该文件的编码是utf-8
<!-- 解读:modelVersion 1. 描述这个pom文件/pom模型遵循的哪个版本 2. 对应maven2 和 maven3 而言, 这里的modelVersion只能是4.0.0 --> <modelVersion>4.0.0</modelVersion> <!-- 解读: 下面是指定该项目的坐标, 是创建项目时,程序员指定 1. groupId: 组织名 2. artifactId: 项目名 3. version: 版本 --> <groupId>com.wedu</groupId> <artifactId>java-project-maven</artifactId> <version>1.0-SNAPSHOT</version> <!-- 解读 1. 这里就是引入项目需要的jar包, 类似传统项目的import jar的作用 2. 在 dependencies标签内, 可以指定多个需要依赖的jar /导入jar 3. 引入的jar包需要一个完整的jar包坐标, 从mvn仓库查询得到 --> <dependencies> <dependency> <!-- 依赖jar的 groupId : 组织名--> <groupId>junit</groupId> <!-- 依赖jar的 artifactId : 项目名--> <artifactId>junit</artifactId> <!-- 依赖jar的 version : 版本--> <version>4.12</version> </dependency>
完成编译
- 执行如下指令,完成maven 项目的编译,会得到源代码对应的class 文件(如图)
- 编程成功,会自动创建target 目录,并生成对应的.class 文件(如图)
- 细节说明: 第一次速度慢,需要下载相关jar,后面就快了.
完成测试
- 执行如下指令,完成maven 项目的测试,(如图)
- 测试指令执行完毕,会生成Test 源文件的class , 还会输出测试结果.
- 细节说明: 第一次速度慢,需要完成一些初始化工作,后面就快了.
完成打包
- 执行如下指令,完成maven 项目的打包,输出jar, 可以供其它模块使用(如图)
- 打包后,在target 目录生成对应的打包文件jar
完成安装
- 为了演示方便, 把当前maven 的settings.xml 的仓库恢复到默认的, 修改D:\program\apache-maven-3.6.3-bin\apache-maven-3.6.3\conf\settings.xml
- 执行install 指令,能完成maven 项目的安装,会把打包得到的jar, 提交到本地仓库
- 说明: 如果没有修改settings.xml 成默认的仓库路径, install 后, 生成的jar 会在指定的仓库路径下.
- 注意当把hello-project-1.0.jar 提交到本地仓库后,该jar 也可以被其他maven 项目使用了,非常方便
完成清理
- 执行如下指令,完成maven 项目的清理,会清除生成的target 目录
- 应用场景,比如我们希望把源码进行拷贝或移植, 或者希望来一次全新的bulid,就可以先进行clean
小结Maven 构建指令
● 说明: Maven 构建命令使用mvn 开头,后面添加功能参数,可以一次执行多个命令,使用空格分隔
mvn compile #编译 mvn clean #清理 mvn test #测试 mvn package #打包 mvn install #安装
IDEA 创建Maven Web 工程
需求说明/图解
使用IDEA 创建Maven WEB 项目, 可以运行hello,world 页面
注意体会Maven WEB 项目和前面创建的Maven java 项目不同
创建Maven web 项目
创建maven web 工程
web-hello-maven 工程
创建成功的maven web 项目结构
创建的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"> <modelVersion>4.0.0</modelVersion> <!-- 解读 1. groupId 、artifactId、version就是 maven项目坐标 2. packaging: 打包方式,默认是jar, 因为当前是web项目,所以这里打包成war --> <groupId>com.wyxedu</groupId> <artifactId>web-hello-maven</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>web-hello-maven Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <!-- maven项目的属性, 根据实际情况,可以修改 --> <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> <dependencies> <!-- 默认引入的jar --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> <!--默认引入的maven插件, 前面说过mvn的各种指令compile/install/test等都是由插件完成的 --> <build> <finalName>web-hello-maven</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
配置Tomcat 服务器&运行
- 为maven web 项目配置tomcat
war:先打包成war 包,再把该war 包部署到服务器上
war exploded:直接把文件夹、class 文件等等移到Tomcat 上进行部署。因此这种方式支持热部署,一般在开发的时候都是使用这种模式
依赖管理
依赖配置
一句话: 依赖指当前项目运行需要的jar,一个项目可以设置多个依赖
依赖的举例
<!-- 解读: 1. 这里就是引入项目需要的jar 包, 类似传统的import jar 的作用 2. 在dependencies 标签内,可以指定多个需要依赖的jar/导入的jar 3. 引入的jar 包需要一个完整的jar 包坐标,从mvn 仓库查询即可得到--> <dependencies> <!-- 引入的一个具体的依赖jar, 包括一个完整的依赖坐标--> <dependency> <!-- 依赖的gruopid: 组织名--> <groupId>junit</groupId> <!-- 依赖的项目名--> <artifactId>junit</artifactId> <!-- 依赖的版本--> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
依赖传递
直接依赖
一句话: 在当前项目中通过依赖配置建立的依赖关系
举例说明
- 创建maven_A java 项目
- 在maven_A 项目中引入mysql5.1.47 , 这时我们就说maven_A 项目直接依赖mysql5.1.47jar
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies>
- 如图
间接依赖
一句话: 当前项目直接依赖资源(比如m1), 而m1 又依赖资源(m2),我们就说当前项目间接依赖资源(m2)
举例说明
- 创建maven_B java 模块(为了好理解,我们放在一个项目中)
- 修改D:\java_projects\maven_B\pom.xml , 引入junit4.12
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
- 修改D:\java_projects\maven_A\pom.xml , 引入maven_B
<dependencies> <!-- 引入maven_B --> <dependency> <groupId>com.wyx</groupId> <artifactId>maven_B</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies>
- 观察maven_A 和maven_B 的依赖情况
依赖冲突
路径优先
- 路径优先∶当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高
- 示意图说明:
- 解释项目A 依赖情况
- 如果1 度资源有junit 4.1 , 而2 度资源有junit4.2
- 根据路径优先原则, 项目A 生效的是junit4.1
- 举例说明.
- 创建maven_C(为了方便观察,仍然放在一个项目中), 创建方式和maven_B 类似,就不截图了
- 修改D:\java_projects\maven_C\pom.xml, 引入junit4.13
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> </dependencies>
- 修改D:\java_projects\maven_B\pom.xml, 引入maven_C
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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>com.wyx</groupId> <artifactId>maven_B</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- 引入maven_C --> <dependency> <groupId>com.wyx</groupId> <artifactId>maven_C</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
- 刷新工程,看看maven_A 生效的就是路径优先的maven_B 的junit4.12
- 为了验证是哪个junit 生效了, 还可以通过代码来验证, 写一段代码D:\java_projects\maven_A\src\main\java\HelloTest.java
声明优先
- 声明优先∶当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的
- 示意图说明:
图解项目A 依赖情况
- 如果1 度(灰框)资源有junit 4.1 , 而1 度(蓝框)资源有junit4.2
- 根据声明优先原则, 要看项目A , 在pom.xml 引入的顺序,先引入的就是生效的
- 举例说明.
- 创建maven_D(为了方便观察,仍然放在一个项目中), 创建方式和maven_B 类似,就不截图了
- 修改D:\java_projects\maven_D\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"> <modelVersion>4.0.0</modelVersion> <groupId>com.wyx</groupId> <artifactId>maven_D</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> </dependencies> </project>
- 修改D:\java_projects\maven_A\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"> <modelVersion>4.0.0</modelVersion> <groupId>com.wyx</groupId> <artifactId>maven_A</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- 引入maven_D --> <dependency> <groupId>com.wyx</groupId> <artifactId>maven_D</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- 引入maven_B --> <dependency> <groupId>com.wyx</groupId> <artifactId>maven_B</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies> </project>
- 为了验证是junit4.13.2 生效还是junit4.12 生效, 写一段代码D:\java_projects\maven_A\src\main\java\HelloTest.java
特殊优先
- 特殊优先∶当同级配置了相同资源的不同版本,后配置的覆盖先配置的(提醒:要尽量避免这种没有意义的冲突)
- 修改D:\java_projects\maven_A\pom.xml, 引入mysql5.1
<?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>com.wyx</groupId> <artifactId>maven_A</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- 引入maven_D --> <dependency> <groupId>com.wyx</groupId> <artifactId>maven_D</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- 引入maven_B --> <dependency> <groupId>com.wyx</groupId> <artifactId>maven_B</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- 引入mysql5.1.43 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.43</version> </dependency> </dependencies> </project>
- 根据特殊优先原则: maven_A 生效的是mysql5.1.43
- 测试完后,注销到引入mysql5.1.43 , 这里是为了验证.
可选依赖
一句话: 可选依赖指对外隐藏当前所依赖的资源- 不透明
举例说明
- 需求: 隐藏maven_D 项目的junit 依赖,不让其他项目来使用
- 修改D:\java_projects\maven_D\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"> <modelVersion>4.0.0</modelVersion> <groupId>com.wyx</groupId> <artifactId>maven_D</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <!-- 解读 1. option 默认是false , 即不隐藏 2. option 设置为true , 即隐藏--> <optional>true</optional> </dependency> </dependencies> </project>
- 刷新maven 管理器,看maven_A 的变化
- 测试完毕后,别忘了恢复原来状态.
排除依赖
一句话: 排除依赖指主动断开依赖的资源, 被排除的资源无需指定版本- 不需要
举例说明
- 需求: maven_A 项目主动排除maven_D 的junit4.13.2 这个依赖
- 修改D:\java_projects\maven_D\pom.xml
<!-- 引入maven_D --> <dependency> <groupId>com.wyx</groupId> <artifactId>maven_D</artifactId> <version>1.0-SNAPSHOT</version> <!--排除junit--> <exclusions> <exclusion> <groupId>junit</groupId> <artifactId>junit</artifactId> </exclusion> </exclusions> </dependency>
- 刷新maven 管理器,看maven_A 的变化, 仍然是拿不到junti4.13.2, 提示: 再通过HelloTest.java 来验证实际使用的junit 版本, 更加清晰了.
可选依赖和排除依赖区别
一句话: 隐藏依赖是不让其他项目来引用(我不给), 排除依赖是主动不要引入的某个资源(我不要)
依赖范围
作用范围
一句话: 依赖的jar 默认情况可以在任何地方使用, 通过scope 标签设定其作用范围
举例说明
● 作用范围说明
- 主程序范围有效(src/main 文件夹范围内)
- 测试程序范围有效(src/test 文件夹范围内)
- 是否参与打包(package 指令范围内)
- compile(默认,在主程序、测试代码、打包都有效)
● 作用范围一览图
解读
- 某个引入的jar 包作用范围是程序员根据,业务的实际需要来设置的,不要认为是固定的。
- 比如log4j jar 包,在主程序,测试代码,打包都是需要的, 因此作用范围应当设置为complie
- junit 只是测试代码需要,因此作用范围设置为test 合适,但是如果程序员认为在主程序和打包就是要junit, 仍然可以设置为默认compile
- 比如servlet-api 是tomcat 自己带的,当把程序打包放到生产环境时,用生产环境tomcat 的servlet-api 即可,所以设置为provided 合适,这样就放在servlet-api 版本冲突.
- 比如jdbc, 是第三方的jar , 打包放在生产环境,就应当在自己的包提供jdbc 驱动包,否则程序会因为少驱动包,运行失败
● 看一个案例, 体会依赖范围的特点.
<?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>com.wyx</groupId> <artifactId>maven_D</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <!-- 解读 1. option 默认是false , 即不隐藏 2. option 设置为true , 即隐藏--> <optional>false</optional> <!-- scope 设置为test, 让他在test 范围有效--> <scope>test</scope> </dependency> </dependencies> </project>
- 创建D:\java_projects\maven_D\src\test\java\T2.java, 在test 范围正常使用
import org.junit.Test; public class T2 { @Test public void f1(){ } }
- 创建D:\java_projects\maven_D\src\main\java\T1.java, 在主程序中,报错
- 修改D:\java_projects\maven_D\pom.xml, 发现主程序和测试程序都可以使用了.
<optional>false</optional> <!-- scope 设置为test, 让他在test范围有效, 默认就是compile,可以不设置--> <scope>compile</scope>
- 运行maven web 项目
Maven 项目构建生命周期
一句话: Maven 构建生命周期描述的是一次构建过程经历了多少个事件
生命周期的3 大阶段
clean
清理工作
default
核心工作,例如编译,测试,打包,部署等
site
产生报告,发布站点等
生命周期是分阶段执行的
一句话: 项目构建生命周期分很多阶段,并不是每次都完整执行,而是根据用户的要求来执行的【比如你执行compile, 那么就执行到complie 这个阶段,如果你执行install, 则会执行compile->test->package->install】
举例说明
1、演示compile
2、演示install
maven 插件
介绍
1、插件与生命周期内的某个阶段绑定,在执行到对应生命周期时, 由对应插件来完成任务/功能.
2、maven 插件很多,先看一张图:
3、通过插件可以自定义其他功能
4、文档: http://maven.apache.org/plugins/index.html
自定义插件-应用实例
需求: 在pom.xml 加入自定义插件,能够在对maven_D 项目打包时,能输出主程序和测试程序的源码
完成步骤
- 当前package 只会得到项目的jar
- 修改D:\java_projects\maven_D\pom.xml, 加入maven 插件并配置(注意: 加入自定义插件后,可能会爆红,重启项目即可.)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" 韩顺平Java 工程师 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>com.wyx</groupId> <artifactId>maven_D</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <!-- 解读 1. option 默认是false , 即不隐藏 2. option 设置为true , 即隐藏--> <optional>false</optional> <!-- scope 设置为test, 让他在test 范围有效--> <scope>compile</scope> </dependency> </dependencies> <!--在build 时,自定义的插件--> <build> <plugins> <plugin> <!-- 插件坐标--> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.2.1</version> <executions> <execution> <goals> <!-- 对主程序输出源码打包--> <goal>jar</goal> <!-- 对测试程序输出源码打包--> <goal>test-jar</goal> </goals> <!-- 在generate-test-resources 阶段执行--> <phase>generate-test-resources</phase> </execution> </executions> </plugin> </plugins> </build> </project>
- 观察一下maven 项目管理器
- 再次执行maven-D 的intall 操作, 会得到两个新的jar ,分别包含了主程序和测试程序的源码。(说明: 测试时,需要保证src/main/… 和src/test/… 两个目录下有java 源代码,否则不会生成源码jar)
- 解压得到jar , 可以看到源码了.
maven 插件-maven 构建生命周期关系图
图解
- 在maven 项目构建生命周期中,每个阶段的执行都有相应的插件完成
- 各个插件执行过程中,会附带输出内容,比如jar/war/xml/源码
- 程序员可以使用maven 默认的插件,也可以自定义插件,完成定制任务.
- 自定义插件引入成功, 是可以看到