概要:
1. maven 基本概念
2. maven 核心配置

一、maven 安装与核心概念

概要:
1.maven 安装
2.maven 编译(compile)
3.执行测试用例(test)
4.maven 打包
5.maven 依懒管理

1、安装:

1.官网下载 Maven (http://maven.apache.org/download.cgi
2.解压指定目录
3.配置环境变量
4.检查安装是否成功 (mvn -version)
maven 是什么?它的基本功能是什么?编译打包测试、依赖管理直观感受一下maven编译打包的过程。

2、maven 编译

maven 编译过程演示
l 创建maven项目。
l 创建src 文件
l 编写 pom 文件
l 执行编译命令

编写pom 文件基础配置

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  4. http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>org.test</groupId>
  7. <artifactId>my-project</artifactId>
  8. <version>1.0.SNAPSHOT</version>
  9. </project>


#mvn 编译命令
mvn compile

—————————————-
[INFO] No sources to compile
[INFO]———————————————————————————————-
[INFO] BUILD SUCCESS
[INFO]———————————————————————————————-
[INFO] Total time: 0.473 s
[INFO] Finished at: 2018-08-05T15:55:44+08:00
[INFO] Final Memory: 6M/153M
[INFO]———————————————————————————————-


请注意,在上述配置和命令当中,我们并没有指定源码文件在哪里?最后编译到哪里去?在这里
maven 采用了约定的方式从指项目结构中获取源码与资源文件进行编译打包。
a.主源码文件:${project}/src/main/java
b.主资源文件:${project}/src/main/resources
c.测试源码文件:${project}/src/test/java
d.测试资源文件:${project}/src/test/resources
将java 文件移至 src/main/java 目录,重新执行编译.
mv src/hello.java /src/main/java/hello.java
mvn compile;

3、Maven打包

maven 打包演示
#mvn 打包命令
mvn package

4、maven 单元测试演示

l 编写测试类
l 执行测试命令

编译测试类
# 创建测试目录
mkdir -p /src/test/java
# 编写 测试类
vim TestHello.java
#测试类代码————————————

  1. package com.test;
  2. public class TestHello{
  3. public void sayHelloTest(){
  4. System.out.println("run test .....");
  5. }
  6. }

执行测试指令:
#执行测试
mvn test
执行完指令发现没有执行我们的测试方法,这是为何?原因在于maven 当中的测试类又做了约定,约定必须是Test开头的类名与test 开头的方法才会执行。
重新修改方法名后 在执行mvn test 即可正常执行。

  1. package com.test;
  2. public class TestHello{
  3. public void testsayHelloTest(){
  4. System.out.println("run test .....");
  5. }
  6. }


通常测试我们是通过junit 来编译测试用例,这时就就需添加junit 的依赖。

5、maven 依赖管理

l 在pom 文件中添加junit 依赖
l 修改测试类,加入junit 代码
l 执行测试命令
加入依懒配置

  1. <dependencies>
  2. <dependency>
  3. <groupId>junit</groupId>
  4. <artifactId>junit</artifactId>
  5. <version>4.0</version>
  6. <scope>test</scope>
  7. </dependency>
  8. </dependencies>


修改测试类引入junit 类.
//引入junit 类
import org.junit.Assert;
import org.junit.Test;
Assert.assertEquals(“”,”hi”);
注意:当我们在classPath 当中加入 junit,原来以test 开头的方法不会被执行,必须加入 @Test注解才能被执行。

提问:
在刚才的演示过程当中 ,junit jar包在哪里?是怎么加入到classPath 当中去的?maven 是在执行test 命令的时间 动态从本地仓库中去引入junit jar 包,如果找不到就会去远程仓库下载,然后在引入。
maven - 图1
默认远程仓库:
默认远程仓库 maven central 其配置在
maven-model-builder-3.2.1.jar\org\apache\maven\model\pom-4.0.0.xml位置
本地仓库位置:
本地仓库位置默认在 ~/.m2/respository 下
要修改 ${M2_HOME}/conf/settings.xml 来指定仓库目录

G:.m2\repository

maven 核心功能总结:
1.maven 核心作用是编译、测试、打包。
2.根目录下的pom.xml 文件设置分组ID与artifactId。
3.maven 基于约定的方式从项目中获取源码与资源文件进行编译打包。
4.对于项目所依懒的组件与会本地仓库引用,如果本地仓库不存在则会从中央仓库下载。

二、maven核心配置

概要:
1.项目依懒(内部、外部)
2.项目聚合与继承
3.项目构建配置
项目依懒
项目依赖是指maven 通过依赖传播、依赖优先原则、可选依赖、排除依赖、依赖范围等特性来管理项目ClassPath。

1、依赖传播特性:

我们的项目通常需要依赖第三方组件,而第三方组件又会依赖其它组件遇到这种情况Maven会将依赖网络中的所有节点都会加入ClassPath当中,这就是Maven的依赖传播特性。
n 举例演示Spring MVC的依赖网络

  1. <!-- 添加spring web mvc演示 -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-webmvc</artifactId>
  5. <version>4.0.4.RELEASE</version>
  6. </dependency>


在刚刚的演示当中,项目直接依赖了spring-webmvc 叫直接依赖,而对commons-logging 依赖是通过webmvc传递的所以叫间接依赖。

2、依赖优先原则

基于依赖传播特性,导致整个依赖网络会很复杂,难免会出现相同组件不同版本的情况。Maven此时会基于依赖优先原则选择其中一个版本。
第一原则:最短路径优先。
第二原则:相同路径下配置在前的优先。
n 第一原则演示

  1. <!-- 直接添加commons-logging-->
  2. <dependency>
  3. <groupId>commons-logging</groupId>
  4. <artifactId>commons-logging</artifactId>
  5. <version>1.2</version>
  6. </dependency>

上述例子中commons-logging 通过spring-webmvc依赖了1.1.3,而项目中直接依赖了1.2,基于最短路径原则项目最终引入的是1.2 版本。
n 第二原则演示:
步骤:
1.添加一个新工程Project B
2.配置Project B 依赖 spring-web.3.2.9.RELEASE
3.当前工程直接依赖 Project B
配置完之后,当前工程 project A 有两条路径可以依赖 spring-web,选择哪一条就取决于 对 webmvc 和 Project B的配置先后顺序。
Project A==> spring-webmvc.4.0.0.RELEASE==> spring-web.4.0.0.RELEASE
Project A==> Project B 1.0.SNAPSHOT ==>spring-web.3.2.9.RELEASE


注意:在同一pom文件,第二原则不在适应。如下配置,最终引用的是1.2 版本,而不是配置在前面的1.1.1版本.

  1. <!-- 在1.2 之前添加 commons-logging -->
  2. <dependency>
  3. <groupId>commons-logging</groupId>
  4. <artifactId>commons-logging</artifactId>
  5. <version>1.1.1</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>commons-logging</groupId>
  9. <artifactId>commons-logging</artifactId>
  10. <version>1.2</version>
  11. </dependency>

3、可选依赖

可选依赖表示这个依赖不是必须的。通过在 true 表示,默认是不可选的。可选依赖不会被传递。
l 演示可选依赖的效果。

4、排除依赖

即排除指定的间接依赖。通过配置 配置排除指定组件。

  1. <!-- 排除指定项目 -->
  2. <exclusions>
  3. <exclusion>
  4. <groupId>org.springframework</groupId>
  5. <artifactId>spring-web</artifactId>
  6. </exclusion>
  7. </exclusions>

l 演示排除依赖

5、依赖范围

像junit 这个组件 我们只有在运行测试用例的时候去要用到,这就没有必要在打包的时候把junit.jar 包过构建进去,可以通过Mave 的依赖范围配置来达到这种目的。maven 总共支持以下四种依赖范围:
compile(默认): 编译范围,编译和打包都会依赖。
provided:提供范围,编译时依赖,但不会打包进去。如:servlet-api.jar
runtime:运行时范围,打包时依赖,编译不会。如:mysql-connector-java.jar
test:测试范围,编译运行测试用例依赖,不会打包进去。如:junit.jar
system:表示由系统中CLASSPATH指定。编译时依赖,不会打包进去。配合一起使用。示例:java.home下的tool.jar

system 除了可以用于引入系统classpath 中包,也可以用于引入系统非maven 收录的第三方Jar,做法是将第三方Jar放置在 项目的 lib 目录下,然后配置 相对路径,但因system 不会打包进去所以需要配合 maven-dependency-plugin 插件配合使用。当然推荐大家还是通过 将第三方Jar手动install 到仓库。

  1. <!-- system 的通常使用方式-->
  2. <dependency>
  3. <groupId>com.sun</groupId>
  4. <artifactId>tools</artifactId>
  5. <version>${java.version}</version>
  6. <scope>system</scope>
  7. <optional>true</optional>
  8. <systemPath>${java.home}/../lib/tools.jar</systemPath>
  9. </dependency>
  10. <!-- system 另外使用方式 ,将工程内的jar直接引入 -->
  11. <dependency>
  12. <groupId>jsr</groupId>
  13. <artifactId>jsr</artifactId>
  14. <version>3.5</version>
  15. <scope>system</scope>
  16. <optional>true</optional>
  17. <systemPath>${basedir}/lib/jsr305.jar</systemPath>
  18. </dependency>
  19. <!-- 通过插件 将system 的jar 打包进去。 -->
  20. <plugin>
  21. <groupId>org.apache.maven.plugins</groupId>
  22. <artifactId>maven-dependency-plugin</artifactId>
  23. <version>2.10</version>
  24. <executions>
  25. <execution>
  26. <id>copy-dependencies</id>
  27. <phase>compile</phase>
  28. <goals>
  29. <goal>copy-dependencies</goal>
  30. </goals>
  31. <configuration>
  32. <outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/lib</outputDirectory>
  33. <includeScope>system</includeScope>
  34. <excludeGroupIds>com.sun</excludeGroupIds>
  35. </configuration>
  36. </execution>
  37. </executions>
  38. </plugin>


#手动加入本地仓库
mvn install:install-file-Dfile=abc_client_v1.20.jar -DgroupId=test-DartifactId=test-client -Dversion=1.20 -Dpackaging=jar

三、项目聚合与继承

1、聚合

是指将多个模块整合在一起,统一构建,避免一个一个的构建。聚合需要个父工程,然后使用 进行配置其中对应的是子工程的相对路径

  1. <modules>
  2. <module>test-client</module>
  3. <module>test-server</module>
  4. </modules>

2、继承

继承是指子工程直接继承父工程 当中的属性、依赖、插件等配置,避免重复配置。
1.属性继承:
2.依赖继承:
3.插件继承:
上面的三个配置子工程都可以进行重写,重写之后以子工程的为准。

3、依赖管理

通过继承的特性,子工程是可以间接依赖父工程的依赖,但多个子工程依赖有时并不一至,这时就可以在父工程中加入 声明该功程需要的JAR包,然后在子工程中引入。

  1. <!-- 父工程中声明 junit 4.12 -->
  2. <dependencyManagement>
  3. <dependencies>
  4. <dependency>
  5. <groupId>junit</groupId>
  6. <artifactId>junit</artifactId>
  7. <version>4.12</version>
  8. </dependency>
  9. </dependencies>
  10. </dependencyManagement>
  11. <!-- 子工程中引入 -->
  12. <dependency>
  13. <groupId>junit</groupId>
  14. <artifactId>junit</artifactId>
  15. </dependency>

4、项目属性:

通过 配置 属性参数,可以简化配置。


ddd


${proName}
maven 默认的属性
${basedir} 项目根目录
${version}表示项目版本;
${project.basedir}同${basedir};
${project.version}表示项目版本,与${version}相同;
${project.build.directory} 构建目录,缺省为target
${project.build.sourceEncoding}表示主源码的编码格式;
${project.build.sourceDirectory}表示主源码路径;
${project.build.finalName}表示输出文件名称;
${project.build.outputDirectory} 构建过程输出目录,缺省为target/classes

项目构建配置
1.构建资源配置
2.编译插件
3.profile 指定编译环境

构建资源配置
基本配置示例:
package
${basedir}/target2
${artifactId}-${version}
说明:
defaultGoal,执行构建时默认的goal或phase,如jar:jar或者package等
directory,构建的结果所在的路径,默认为${basedir}/target目录
finalName,构建的最终结果的名字,该名字可能在其他plugin中被改变

配置示例

  1. <resources>
  2. <resource>
  3. <directory>src/main/java</directory>
  4. <includes>
  5. <include>**/*.MF</include>
  6. <include>**/*.XML</include>
  7. </includes>
  8. <filtering>true</filtering>
  9. </resource>
  10. <resource>
  11. <directory>src/main/resources</directory>
  12. <includes>
  13. <include>**/*</include>
  14. <include>*</include>
  15. </includes>
  16. <filtering>true</filtering>
  17. </resource>
  18. </resources>

说明:
l resources,build过程中涉及的资源文件
¡ targetPath,资源文件的目标路径
¡ directory,资源文件的路径,默认位于${basedir}/src/main/resources/目录下
¡ includes,一组文件名的匹配模式,被匹配的资源文件将被构建过程处理
¡ excludes,一组文件名的匹配模式,被匹配的资源文件将被构建过程忽略。同时被includes和excludes匹配的资源文件,将被忽略。
¡ filtering: 默认false ,true 表示 通过参数 对 资源文件中 的${key} 在编译时进行动态变更。替换源可 紧 -Dkey 和pom 中的 值 或 中指定的properties 文件。

版本管理

  1. 二方包全部使用明确的版本号,不要使用属性引用
  2. 多模块项目所有包管理都在父pom中定义,子模块中按需引用(提前确定包的版本,不会出现不同模块依赖的jar包版本不一致)
  3. 子模块依赖确定好依赖层级关系,重复包不需要再次引用(让依赖更清晰,便于维护)
  4. 不要在非最底层模块排除底层模块引入的包(容易出现编译不报错,启动或者是运行时找不到类或者是版本不正确的情况)

image.png

jar包冲突

jar包冲突有两种,一种是同一个包的版本冲突,另一种是不同包的类冲突

启动或者运行中报ClassNotFoundException,NoSuchMethodError说明出现了jar包冲突

首先明确一个概念,我们最终打包里面的jar包都是由最上层模块的依赖结果得到的,比如在上图的依赖关系情况下最终打包的结果是以子模块4的依赖为准

  1. ClassNotFoundException
    1. 版本过低,低版本中没有这个类。比如子模块3依赖了a-2.0.jar,但是子模块4中依赖变成了a-1.0.jar,编译不会报错,但是最终打包之后运行期就会出现异常。
    2. jar包不存在,根本不存在这个类。比如子模块3依赖了a-2.0.jar,但是在子模块4中将a-2.0.jar做了排包,则最终的打包里面没有包括a-2.0.jar的包,导致出现异常
  2. NoSuchMethodError
    1. 同ClassNotFoundException的第一个场景,由于版本过低导致的异常

jar包冲突处理

现在aone提供了jar包依赖发生变化的提示,应用正常启动的情况下我们也要关注这里,看看是否出现某个包被移除或者是版本出现下降的情况,根据情况做出选择 调整依赖或者属于正常
如果确认已经出现jar包冲突,首先根据报错提示的类找到对应的jar包,通过全限定类名搜索减少干扰
image.png

确定所属的包之后分析jar包是在哪个子模块依赖出现问题的,并进行调整,有两种方式辅助定位

  1. 打印依赖树,分析依赖 mvn dependency:tree -Dverbose
  2. 通过idea的插件 maven helper

如何减少jar包冲突带来的风险

  1. 使用依赖管理标签在父pom中统一声明依赖的包,进行版本锁定,子模块全部使用无版本号的依赖
  2. 尽量在父模块中进行排包
  3. 各个子模块直接的依赖关系要清晰,尽量不要出现互相依赖的情况
  4. 各个子模块做增量依赖引入,不要单独引入依赖的子模块已经引入的包
  5. 上层模块不要在依赖的下层模块的jar包上面做排包(容易出现运行时异常)