Maven
简介
Maven 是 Apache 软件基金会组织维护的一款专门为 Java 项目提供构建和依赖管理支持的工具
构建
Java 项目开发过程中,构建指的是使用**『原材料生产产品』**的过程。
-
原材料
- Java 源代码
- 基于 HTML 的 Thymeleaf 文件
- 图片
- 配置文件
-
产品
- 一个可以在服务器上运行的项目
-
清理:删除上一次构建的结果,为下一次构建做好准备
-
编译:Java 源程序编译成 *.class 字节码文件
-
测试:运行提前准备好的测试程序
-
报告:针对刚才测试的结果生成一个全面的信息
-
打包
- Java工程:jar包
- Web工程:war包
-
安装:把一个 Maven 工程经过打包操作生成的 jar 包或 war 包存入 Maven 仓库
-
部署
- 部署 jar 包:把一个 jar 包部署到 Nexus 私服服务器上
- 部署 war 包:借助相关 Maven 插件(例如 cargo),将 war 包部署到 Tomcat 服务器上
依赖
依赖管理中要解决的具体问题:
- jar 包的下载:使用 Maven 之后,jar 包会从规范的远程仓库下载到本地
- jar 包之间的依赖:通过依赖的传递性自动完成
- jar 包之间的冲突:通过对依赖的配置进行调整,让某些jar包不会被导入
命令行环境
坐标
使用三个**『向量』在『Maven的仓库』中唯一的定位到一个『jar』**包。
- groupId:公司或组织的 id
- artifactId:一个项目或者是项目中的一个模块的 id
- version:版本号
三个向量的取值方式
- groupId:公司或组织域名的倒序,通常也会加上项目名称
- 例如:com.atguigu.maven
- artifactId:模块的名称,将来作为 Maven 工程的工程名
- version:模块的版本号,根据自己的需要设定
- 例如:SNAPSHOT 表示快照版本,正在迭代过程中,不稳定的版本
- 例如:RELEASE 表示正式版本
实例
坐标:
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
上面坐标对应的 jar 包在 Maven 本地仓库中的位置:
Maven本地仓库根目录\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar
命令生成Maven工程
# 主命令 插件:目标
mvn archetype:generate
Define value for property 'groupId': com.lrdhappy.maven
Define value for property 'artifactId': mavenlearn
Define value for property 'version' 1.0-SNAPSHOT: :
Define value for property 'package' com.lrdhappy.maven: :
Confirm properties configuration:
groupId: com.lrdhappy.maven
artifactId: mavenlearn
version: 1.0-SNAPSHOT
package: com.lrdhappy.maven
生成pom.xml
<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>
<!-- 当前Maven工程的坐标 -->
<groupId>com.lrdhappy.maven</groupId>
<artifactId>mavenlearn</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 当前Maven工程的打包方式,可选值有下面三种: -->
<!-- jar:表示这个工程是一个Java工程 -->
<!-- war:表示这个工程是一个Web工程 -->
<!-- pom:表示这个工程是“管理其他工程”的工程 -->
<packaging>jar</packaging>
<name>mavenlearn</name>
<url>http://maven.apache.org</url>
<properties>
<!-- 工程构建过程中读取源码时使用的字符集 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 当前工程所依赖的jar包 -->
<dependencies>
<!-- 使用dependency配置一个具体的依赖 -->
<dependency>
<!-- 在dependency标签内使用具体的坐标依赖我们需要的一个jar包 -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<!-- scope标签配置依赖的范围 -->
<scope>test</scope>
</dependency>
</dependencies>
</project>
调整junit版本可直接修改
<dependencies>
<!-- dependency单数标签:配置一个具体的依赖 -->
<dependency>
<!-- 通过坐标来依赖其他jar包 -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!-- 依赖的范围 -->
<scope>test</scope>
</dependency>
</dependencies>
POM简介
POM:Project Object Model,项目对象模型。和 POM 类似的是:DOM(Document Object Model),文档对象模型。它们都是模型化思想的具体体现
模型化思想:
POM 表示将工程抽象为一个模型,再用程序中的对象来描述这个模型。
目录结构
Maven 对于目录结构这个问题,没有采用配置的方式,而是基于约定
Java工程构建
运行 Maven 中和构建操作相关的命令时,必须进入到 pom.xml 所在的目录。
清理
mvn clean
#效果:删除 target 目录
编译
主程序编译:mvn compile
测试程序编译:mvn test-compile
主体程序编译结果存放的目录:target/classes
测试程序编译结果存放的目录:target/test-classes
测试
mvn test
测试的报告存放的目录:target/surefire-reports
打包
mvn package
打包的结果——jar 包,存放的目录:target
安装
mvn install
安装的效果是将本地构建过程中生成的 jar 包存入 Maven 本地仓库。这个 jar 包在 Maven 仓库中的路径是根据它的坐标生成的。
D:\maven-repository\com\lrdhappy\maven\mavenlearn\1.0-SNAPSHOT
安装操作还会将 pom.xml 文件转换为 XXX.pom 文件一起存入本地仓库。所以我们在 Maven 的本地仓库中想看一个 jar 包原始的 pom.xml 文件时,查看对应 XXX.pom 文件即可,它们是名字发生了改变,本质上是同一个文件。
创建Web工程
说明
-
使用 mvn archetype:generate 命令生成 Web 工程时,需要使用一个专门的 archetype
-
参数 archetypeGroupId、archetypeArtifactId、archetypeVersion 用来指定现在使用的 maven-archetype-webapp 的坐标
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.4
Define value for property 'groupId': com.lrdhappy.web
Define value for property 'artifactId': mavenweb
Define value for property 'version' 1.0-SNAPSHOT: :
Define value for property 'package' com.lrdhappy.web: :
Confirm properties configuration:
groupId: com.lrdhappy.web
artifactId: mavenweb
version: 1.0-SNAPSHOT
package: com.lrdhappy.we
创建Servlet
- 在 main 目录下创建 java 目录
package com.lrdhappy.web;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
public class HelloServlet extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("hello maven web");
}
}
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.lrdhappy.web.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
</web-app>
<html>
<body>
<h2>Hello World!</h2>
<a href="helloServlet">Access Servlet</a>
</body>
</html>
编译
直接执行 mvn compile 命令出错
程序包 javax.servlet.http 不存在
程序包 javax.servlet 不存在
配置对 servlet-api.jar 包的依赖
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
加入 pom.xml。重新执行 mvn compile 命令
将 Web 工程打包为 war 包
运行 mvn package 命令,生成 war 包的位置在target目录下
Tomcat运行
-
将 war 包复制到 Tomcat/webapps 目录下
-
启动tomcat
让 Web 工程依赖 Java 工程
本质上来说,Web 工程依赖的 Java 工程其实就是 Web 工程里导入的 jar 包。最终 Java 工程会变成 jar 包,放在 Web 工程的 WEB-INF/lib 目录下
操作
pom.xml 中,在 dependencies 标签中做如下配置:
<!-- 配置对Java工程pro01-maven-java的依赖 -->
<!-- 具体的配置方式:在dependency标签内使用坐标实现依赖 -->
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>pro01-maven-java</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
创建目录
- 创建目录pro02-maven-web**\src\test\java\com\atguigu\maven**
- 把 Java 工程的 CalculatorTest.java 类复制到 pro02-maven-wb**\src\test\java\com\atguigu\maven** 目录下
#测试
mvn test
#打包
mvn package
#查看依赖的jar包
mvn dependency:list
#以树形结构查看
mvn dependency:tree
#org.hamcrest:hamcrest-core:jar:1.3:test
#groupId:artifactId:打包方式:version:依赖的范围
测试依赖
范围
标签的位置:dependencies/dependency/scope
标签的可选值:compile/test/provided/system/runtime/import
compile 和 test 对比
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
---|---|---|---|---|
compile | 有效 | 有效 | 有效 | 有效 |
test | 无效 | 有效 | 有效 | 无效 |
compile 和 provided 对比
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
---|---|---|---|---|
compile | 有效 | 有效 | 有效 | 有效 |
provided | 有效 | 有效 | 有效 | 无效 |
compile:通常使用的第三方框架的 jar 包这样在项目实际运行时真正要用到的 jar 包都是以 compile 范围进行依赖的。比如 SSM 框架所需jar包。
test:测试过程中使用的 jar 包,以 test 范围依赖进来。比如 junit。
provided:在开发过程中需要用到的“服务器上的 jar 包”通常以 provided 范围依赖进来。比如 servlet-api、jsp-api。而这个范围的 jar 包之所以不参与部署、不放进 war 包,就是避免和服务器上已有的同
传递性
在 A 依赖 B,B 依赖 C 的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围。
- B 依赖 C 时使用 compile 范围:可以传递
- B 依赖 C 时使用 test 或 provided 范围:不能传递,所以需要这样的 jar 包时,就必须在需要的地方明确配置依赖才可以。
依赖排除
当 A 依赖 B,B 依赖 C 而且 C 可以传递到 A 的时候,A 不想要 C,需要在 A 里面把 C 排除掉。而往往这种情况都是为了避免 jar 包之间的冲突。所以配置依赖的排除其实就是阻止某些 jar 包的传递。因为这样的 jar 包传递过来会和其他 jar 包冲突
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>pro01-maven-java</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
<!-- 使用excludes标签配置依赖的排除 -->
<exclusions>
<!-- 在exclude标签中配置一个具体的排除 -->
<exclusion>
<!-- 指定要排除的依赖的坐标(不需要写version) -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
继承
概念
Maven工程之间,A 工程继承 B 工程
- B 工程:父工程
- A 工程:子工程
本质上是 A 工程的 pom.xml 中的配置继承了 B 工程中 pom.xml 的配置
操作
-
创建父工程
工程创建好之后,要修改它的打包方式:
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
<packaging>pom</packaging>
**只有打包方式为 pom 的 Maven 工程能够管理其他 Maven 工程。**打包方式为 pom 的 Maven 工程中不写业务代码,它是专门管理其他 Maven 工程的工程。
- 创建模块工程
模块工程类似于 IDEA 中的 module,所以需要进入父工程的根目录,然后运行 mvn archetype:generate 命令来创建模块工程。
- 建立子文件夹,创建子工程
<modules>
<module>pro04-maven-module</module>
<module>pro05-maven-module</module>
<module>pro06-maven-module</module>
</modules>
子工程pom.xml
<!-- 使用parent标签指定当前工程的父工程 -->
<parent>
<!-- 父工程的坐标 -->
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<!-- 子工程的坐标 -->
<!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 -->
<!-- <groupId>com.atguigu.maven</groupId> -->
<artifactId>pro04-maven-module</artifactId>
<!-- <version>1.0-SNAPSHOT</version> -->
- 在父工程中配置依赖的统一管理
<!-- 使用dependencyManagement标签配置对依赖的管理 -->
<!-- 被管理的依赖并没有真正被引入到工程 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
- 子工程中引用那些被父工程管理的依赖
关键点:省略版本号
<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。 -->
<!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
<!-- 具体来说是由父工程的dependencyManagement来决定。 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
</dependencies>
- 父工程中声明自定义属性
<!-- 通过自定义属性,统一指定Spring的版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 自定义标签,维护Spring版本数据 -->
<atguigu.spring.version>4.3.6.RELEASE</atguigu.spring.version>
</properties>
在需要的地方使用${}的形式来引用自定义的属性名
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${atguigu.spring.version}</version>
</dependency>
聚合
使用一个“总工程”将各个“模块工程”汇集起来,作为一个整体对应完整的项目。
- 项目:整体
- 模块:部分
从继承关系角度来看:
- 父工程
- 子工程
从聚合关系角度来看:
- 总工程
- 模块工程
好处
- 一键执行 Maven 命令:很多构建命令都可以在“总工程”中一键执行
- 以 mvn install 命令为例:Maven 要求有父工程时先安装父工程;有依赖的工程时,先安装被依赖的工程。我们自己考虑这些规则会很麻烦。但是工程聚合之后,在总工程执行 mvn install 可以一键完成安装,而且会自动按照正确的顺序执行。
- 配置聚合之后,各个模块工程会在总工程中展示一个列表,让项目中的各个模块一目了然
配置
在总工程中配置 modules 即可:
<modules>
<module>pro04-maven-module</module>
<module>pro05-maven-module</module>
<module>pro06-maven-module</module>
</modules>
依赖循环问题
如果 A 工程依赖 B 工程,B 工程依赖 C 工程,C 工程又反过来依赖 A 工程,那么在执行构建操作时会报下面的错误:循环引用
IDEA环境
创建父工程
-
新建项目–>Maven
-
名称 idea
-
groudid com.lrdhappy.idea
-
artfactid webidea
-
-
开启自动导入(默认开启)
-
选择Maven版本
创建子工程
-
选择父模块
-
修改打包方式
- Web 模块将来打包当然应该是 war 包
<packaging>war</packaging>
-
项目结构设置—>工件
- 给子模块添加到web工件
- facet —>web
- 设置Web资源根目录
- Web 资源的根目录需要设置为 src/main/webapp 目录
- 设置Web模块部署描述符(web.xml)
- G:\maven\idea\t3\src\main\webapp\WEB-INF\web.xml
- 设置Web资源根目录
执行Maven命令
- 侧边栏Maven直接点击对应ui命令
- 侧边栏Maven—>点击M图标,输入框输入
可以在此分析依赖关系
其他概念
生命周期
生命周期名称 | 作用 |
---|---|
Clean | 清理操作相关 |
Site | 生成站点相关 |
Default | 主要构建过程 |
插件
-
Maven 的核心程序仅仅负责宏观调度,不做具体工作。具体工作都是由 Maven 插件完成的。例如:编译过程
-
一个插件可以对应多个目标,而每一个目标都和生命周期中的某一个环节对应。
仓库
- 本地仓库:在当前电脑上,为电脑上所有 Maven 工程服务
- 远程仓库:需要联网
建议:不要中央仓库和阿里云镜像混用,否则 jar 包来源不纯,彼此冲突。
POM 深入与强化
重识Maven
Maven 本身的产品定位是一款项目管理工具
Maven 提供了如下这些功能:
- 项目对象模型(POM):将整个项目本身抽象、封装为应用程序中的一个对象,以便于管理和操作。
- 全局性构建逻辑重用:Maven 对整个构建过程进行封装之后,程序员只需要指定配置信息即可完成构建。让构建过程从 Ant 的『编程式』升级到了 Maven 的『声明式』。
- 构件的标准集合:在 Maven 提供的标准框架体系内,所有的构件都可以按照统一的规范生成和使用。
- 构件关系定义:Maven 定义了构件之间的三种基本关系,让大型应用系统可以使用 Maven 来进行管理
- 继承关系:通过从上到下的继承关系,将各个子构件中的重复信息提取到父构件中统一管理
- 聚合关系:将多个构件聚合为一个整体,便于统一操作
- 依赖关系:Maven 定义了依赖的范围、依赖的传递、依赖的排除、版本仲裁机制等一系列规范和标准,让大型项目可以有序容纳数百甚至更多依赖
- 插件目标系统:Maven 核心程序定义抽象的生命周期,然后将插件的目标绑定到生命周期中的特定阶段,实现了标准和具体实现解耦合,让 Maven 程序极具扩展性
- 项目描述信息的维护:我们不仅可以在 POM 中声明项目描述信息,更可以将整个项目相关信息收集起来生成 HTML 页面组成的一个可以直接访问的站点。这些项目描述信息包括:
- 公司或组织信息
- 项目许可证
- 开发成员信息
- issue 管理信息
- SCM 信息
POM 的四个层次
超级POM
-
POM 即使没有明确指定一个父工程(父 POM),其实也默认继承了超级 POM。
-
超级 POM定义了源文件存放的目录、测试源文件存放的目录、构建输出的目录……
父POM
和 Java 类一样,POM 之间其实也是单继承的
有效 POM
在 POM 的继承关系中,子 POM 可以覆盖父 POM 中的配置;如果子 POM 没有覆盖,那么父 POM 中的配置将会被继承。按照这个规则,继承关系中的所有 POM 叠加到一起,就得到了一个最终生效的 POM。
#查看有效
mvn help:effective-pom
pom.xml
当前 pom.xml 配置的 POM:最多关注和最多使用的一层
属性的声明与引用
help插件
目标 | 说明 |
---|---|
help:active-profiles | 列出当前已激活的 profile |
help:all-profiles | 列出当前工程所有可用 profile |
help:describe | 描述一个插件和/或 Mojo 的属性 |
help:effective-pom | 以 XML 格式展示有效 POM |
help:effective-settings | 为当前工程以 XML 格式展示计算得到的 settings 配置 |
help:evaluate | 计算用户在交互模式下给出的 Maven 表达式 |
help:system | 显示平台详细信息列表,如系统属性和环境变量 |
<properties>
<com.atguigu.hello>good morning maven</com.atguigu.hello>
</properties>
mvn help:evaluate
#输入
${com.atguigu.hello}
#输出
good morning maven
查看系统属性
Java查看系统属性
Properties properties = System.getProperties();
Set<Object> propNameSet = properties.keySet();
for (Object propName : propNameSet) {
String propValue = properties.getProperty((String) propName);
System.out.println(propName + " = " + propValue);
}
Maven访问
mvn help:evaluate
#输入
${java.runtime.name}
${env.JAVA_HOME}
访问project属性
使用表达式 ${project.xxx} 可以访问当前 POM 中的元素值
#一级标签
${project.标签名}
#子标签
${project.标签名.子标签名}
#列表标签
${project.标签名[下标]}
访问全局配置
${settings.标签名} 可以访问 settings.xml 中配置的元素值
build 标签详解
定义约定的目录结构
目录名 | 作用 |
---|---|
sourceDirectory | 主体源程序存放目录 |
scriptSourceDirectory | 脚本源程序存放目录 |
testSourceDirectory | 测试源程序存放目录 |
outputDirectory | 主体源程序编译结果输出目录 |
testOutputDirectory | 测试源程序编译结果输出目录 |
resources | 主体资源文件存放目录 |
testResources | 测试资源文件存放目录 |
directory | 构建结果输出目录 |
<sourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\java</sourceDirectory>
<scriptSourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\scripts</scriptSourceDirectory>
<testSourceDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\src\test\java</testSourceDirectory>
<outputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\classes</outputDirectory>
<testOutputDirectory>D:\idea2019workspace\atguigu-maven-test-prepare\target\test-classes</testOutputDirectory>
<resources>
<resource>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\src\main\resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\src\test\resources</directory>
</testResource>
</testResources>
<directory>D:\idea2019workspace\atguigu-maven-test-prepare\target</directory>
备用插件管理
pluginManagement 标签存放着几个极少用到的插件:
- maven-antrun-plugin
- maven-assembly-plugin
- maven-dependency-plugin
- maven-release-plugin
通过 pluginManagement 标签管理起来的插件就像 dependencyManagement 一样,子工程使用时可以省略版本号,起到在父工程中统一管理版本的效果
声明周期插件
plugins 标签存放的是默认生命周期中实际会用到的插件
- artifactId 和 version 标签定义了插件的坐标,作为 Maven 的自带插件这里省略了 groupId
- executions 标签内可以配置多个 execution 标签,execution 标签内:
- id:指定唯一标识
- phase:关联的生命周期阶段
- goals/goal:关联指定生命周期的目标
- goals 标签中可以配置多个 goal 标签,表示一个生命周期环节可以对应当前插件的多个目标。
依赖配置
依赖范围
import
管理依赖最基本的办法是继承父工程,但是和 Java 类一样,Maven 也是单继承的。如果不同体系的依赖信息封装在不同 POM 中了,没办法继承多个父工程怎么办?这时就可以使用 import 依赖范围。
典型案例当然是在项目中引入 SpringBoot、SpringCloud 依赖:
<dependencyManagement>
<dependencies>
<!-- SpringCloud 依赖导入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringCloud Alibaba 依赖导入 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot 依赖导入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
import 依赖范围使用要求:
- 打包类型必须是 pom
- 必须放在 dependencyManagement 中
runtime
专门用于编译时不需要,但是运行时需要的 jar 包。比如:编译时我们根据接口调用方法,但是实际运行时需要的是接口的实现类。典型案例是:
<!--热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
自定义插件
生产实践
搭建 Maven 私服
jar包冲突问题
抛异常:找不到类
此时抛出的常见的异常类型:
- java.lang.ClassNotFoundException:编译过程中找不到类
- java.lang.NoClassDefFoundError:运行过程中找不到类
- java.lang.LinkageError:不同类加载器分别加载的多个类有相同的全限定名
抛异常:找不到方法
程序找不到符合预期的方法。这种情况多见于通过反射调用方法,所以经常会导致:java.lang.NoSuchMethodError。比如 antlr:antlr:x.x.x 这个包中有一个接口:antlr.collections.AST
版本 | getLine()方法 |
---|---|
2.7.2 | 无 |
2.7.6 | 有 |
没报错但结果不对
发生这种情况比较典型的原因是:两个 jar 包中的类分别实现了同一个接口,这本来是很正常的。但是问题在于:由于没有注意命名规范,两个不同实现类恰巧是同一个名字。
本质
- 同一jar包的不同版本
- 不同jar包中包含相同的类
体系外 jar 包引入
实际开发中确实有可能用到一些 jar 包并非是用 Maven 的方式发布
- 使用install插件设置install-file目标:
mvn install:install-file -Dfile=D:\idea2019workspace\atguigu-maven-outer\out\artifacts\atguigu_maven_outer\atguigu-maven-outer.jar ^
-DgroupId=com.atguigu.maven ^
-DartifactId=atguigu-maven-outer ^
-Dversion=1 ^
-Dpackaging=jar
- 引用
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>atguigu-maven-outer</artifactId>
<version>1</version>
</dependency>
案例
单一架构案例
持久化层所需依赖
- mysql:mysql-connector-java:5.1.37
- com.alibaba:druid:1.2.8
- commons-dbutils:commons-dbutils:1.6
表述层所需依赖
- javax.servlet:javax.servlet-api:3.1.0
- org.thymeleaf:thymeleaf:3.0.11.RELEASE
辅助功能所需依赖
- junit:junit:4.12
- ch.qos.logback:logback-classic:1.2.3
最终完整依赖信息
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
建包
package 功能 | package 名称 |
---|---|
主包 | com.atguigu.imperial.court |
子包[实体类] | com.atguigu.imperial.court.entity |
子包[Servlet基类包] | com.atguigu.imperial.court.servlet.base |
子包[Servlet模块包] | com.atguigu.imperial.court.servlet.module |
子包[Service接口包] | com.atguigu.imperial.court.service.api |
子包[Service实现类包] | com.atguigu.imperial.court.service.impl |
子包[Dao接口包] | com.atguigu.imperial.court.dao.api |
子包[Dao实现类包] | com.atguigu.imperial.court.dao.impl |
子包[Filter] | com.atguigu.imperial.court.filter |
子包[异常类包] | com.atguigu.imperial.court.exception |
子包[工具类] | com.atguigu.imperial.court.util |
子包[测试类] | com.atguigu.imperial.court.test |
打包部署
#跳过测试打包
mvn clean package -Dmaven.test.skip=true
人为指定最终 war 包名称
<!-- 对构建过程进行自己的定制 -->
<build>
<!-- 当前工程在构建过程中使用的最终名称 -->
<finalName>demo-me</finalName>
</build>
上传web到tomcat
启动tomcat
SSM 整合伪分布式案例
工程清单
工程名 | 地位 | 说明 |
---|---|---|
demo-imperial-court-ssm-show | 父工程 | 总体管理各个子工程 |
demo-module01-web | 子工程 | 唯一的 war 包工程 |
demo-module02-component | 子工程 | 管理项目中的各种组件 |
demo-module03-entity | 子工程 | 管理项目中的实体类 |
demo-module04-util | 子工程 | 管理项目中的工具类 |
demo-module05-environment | 子工程 | 框架环境所需依赖 |
demo-module06-generate | 子工程 | Mybatis 逆向工程 |
父工程
各子工程创建好之后就会有下面配置,不需要手动编辑:
<groupId>com.atguigu</groupId>
<artifactId>demo-imperial-court-ssm-show</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>demo-module01-web</module>
<module>demo-module02-component</module>
<module>demo-module03-entity</module>
<module>demo-module04-util</module>
<module>demo-module05-environment</module>
<module>demo-module06-generate</module>
</modules>
Mybatis 逆向工程
demo-module06-generate
<!-- 依赖MyBatis核心包 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
环境依赖工程
demo-module05-environment
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- Spring 持久化层所需依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
<!-- Mybatis 和 Spring 的整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
组件工程
demo-module02-component
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>demo-module03-entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>demo-module04-util</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>demo-module05-environment</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
工具类工程
无配置。
实体类工程
无配置
Web工程
demo-module01-web
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>demo-module02-component</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- junit5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!-- Spring 的测试功能 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
微服务架构案例
Q.E.D.