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 表示将工程抽象为一个模型,再用程序中的对象来描述这个模型。

目录结构

./images

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 包,就是避免和服务器上已有的同

./images

传递性

在 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 的配置

操作

  1. 创建父工程

    工程创建好之后,要修改它的打包方式:

  <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 工程的工程。

  1. 创建模块工程

模块工程类似于 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> -->
  1. 在父工程中配置依赖的统一管理
<!-- 使用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>
  1. 子工程中引用那些被父工程管理的依赖

关键点:省略版本号

<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。	-->
<!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
<!-- 具体来说是由父工程的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>
  1. 父工程中声明自定义属性
<!-- 通过自定义属性,统一指定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

执行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 私服

Nexus

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 逆向工程

images

父工程

各子工程创建好之后就会有下面配置,不需要手动编辑:

<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>

微服务架构案例

代码重工