导言: 生产环境下开发不再是一个项目一个工程,而是每一个模块创建一个工程, 而多个模块整合在一起就需要 使用到像 Maven 这样的构建工具。
1 Why?
1.1 真的需要吗?
Maven 是干什么用的?这是很多同学在刚开始接触 Maven 时最大的问题。之所以会提出这个问题,是因为即使不使用 Maven 我们仍然可以进行 B/S 结构项目的开发。从表述层、业务逻辑层到持久化层再到数据库都有成熟的解决方案——不使用 Maven 我们一样可以开发项目啊?
1.2 maven解决的问题:
目前的技术在开发中存在网速度问题:
- 一个项目就是一个工程
- 项目中需要的jar包必须手动复制、粘贴到
WEB-INF/lib
目录下 - jar包需要别人替我们准备好,或者官网下载
- 一个jar包一块的其他jar包需要自己手动加入到项目中
①添加第三方 jar 包
在今天的 JavaEE 开发领域,有大量的第三方框架和工具可以供我们使用。要使用这些 jar 包最简单的方法就是复制粘贴到 WEB-INF/lib
目录下。但是这会导致【每次创建一个新的工程】就需要将 jar 包重复复制到 lib 目录下,从而造成工作区中存在大量重复的文件, 让我们的工程显得很臃肿。
而使用 Maven 后每个 jar 包本身只在本地仓库中保存一份,需要 jar 包的工程只需要以坐标的方式简单的引用一下就可以了。不仅极大的节约了存储空间,让项目更轻巧,更避免了重复文件太多而造成的混乱。
②jar 包之间的依赖关系
jar 包往往不是孤立存在的,很多 jar 包都需要在其他 jar 包的支持下才能够正常工作,我们称之为jar 包之间的依赖关系。最典型的例子是: commons-fileupload-1.3.jar
依赖于 commons-io-2.0.1.jar
,如果没有 IO 包,FileUpload 包就不能正常工作。
那么问题来了,你知道你所使用的所有 jar 包的依赖关系吗?当你拿到一个新的从未使用过的 jar包,你如何得知他需要哪些 jar 包的支持呢?如果不了解这个情况,导入的 jar 包不够,那么现有的程序将不能正常工作。再进一步,当你的项目中需要用到上百个 jar 包时,你还会人为的,手工的逐一确认它们依赖的其他 jar 包吗?这简直是不可想象的。
而引入 Maven 后, Maven 就可以替我们自动的将当前 jar 包所依赖的其他所有 jar 包全部导入进来,无需人工参与 ,节约了我们大量的时间和精力 。用实际例子来说明就是:通过 Maven 导入commons-fileupload-1.3.jar 后, commons-io-2.0.1.jar 会被自动导入,程序员不必了解这个依赖关系。
下图是 Spring 所需 jar 包的部分依赖关系
③获取第三方 jar 包
JavaEE 开发中需要使用到的 jar 包种类繁多,几乎每个 jar 包在其本身的官网上的获取方式都不尽相同。 为了查找一个 jar 包找遍互联网, 身心俱疲, 没有经历过的人或许体会不到这种折磨。 不仅如此,费劲心血找的 jar 包里有的时候并没有你需要的那个类,又或者又同名的类没有你要的方法——以不规范的方式获取的 jar 包也往往是不规范的。
所有知名框架或第三方工具的jar包已经按照统一的规范放在了maven的中央仓库中。使用 Maven 我们可以享受到一个完全统一规范的 jar 包管理体系。 你只需要在你的项目中以坐标的方式依赖一个 jar 包, Maven 就会自动从中央仓库进行下载,并同时下载这个 jar 包所依赖的其他 jar 包 ——规范、完整、准确!一次性解决所有问题!
④将项目拆分成多个工程模块
随着 JavaEE 项目的规模越来越庞大,开发团队的规模也与日俱增。一个项目上千人的团队持续开发很多年对于JavaEE 项目来说再正常不过。那么我们想象一下:几百上千的人开发的项目是同一个 Web工程。那么架构师、项目经理该如何划分项目的模块、如何分工呢?这么大的项目已经不可能通过package 结构来划分模块,必须将项目拆分成多个工程协同开发。多个模块工程中有的是 Java 工程,有的是 Web 工程。
那么工程拆分后又如何进行互相调用和访问呢?这就需要用到 Maven 的依赖管理机制。借助于maven我们可以将一个项目拆分成多个工程。大家请看我们的 Survey 调查项目拆分的情况:
上层模块依赖下层,所以下层模块中定义的 API 都可以为上层所调用和访问
2 Maven简介
2.1 Maven 简介
Maven 是 Apache 软件基金会组织维护的一款自动化构建工具, 专注服务于 Java 平台的项目构建和依赖管理。 Maven 这个单词的本意是: 专家,内行。 读音是['meɪv(ə)n]或['mevn]
2.2 什么是构建
构建并不是创建,创建一个工程并不等于构建一个项目。 要了解构建的含义我们应该由浅入深的从以下三个层面来看:
①纯 Java 代码
大家都知道,我们 Java 是一门编译型语言, .java 扩展名的源文件需要编译成.class 扩展名的字节码文件才能够执行。 所以编写任何 Java 代码想要执行的话就必须经过编译得到对应的.class 文件。
②Web 工程
当我们需要通过浏览器访问 Java 程序时就必须将包含 Java 程序的 Web 工程编译的结果“拿”到服务器上的指定目录下,并启动服务器才行。 这个“拿”的过程我们叫部署。
我们可以将未编译的 Web 工程比喻为一只生的鸡,编译好的 Web 工程是一只煮熟的鸡, 编译部署的过程就是将鸡炖熟。
Web 工程和其编译结果的目录结构对比见下图 :
编译映射
③实际项目
在实际项目中整合第三方框架, Web 工程中除了 Java 程序和 JSP 页面、 图片等静态资源之外,还包括第三方框架的 jar 包以及各种各样的配置文件。 所有这些资源都必须按照正确的目录结构部署到服务器上, 项目才可以运行。
所以综上所述: 构建就是以我们编写的 Java 代码、框架配置文件、 国际化等其他资源文件、 JSP 页面和图片等静态资源作为“原材料”, 去“生产”出一个可以运行的项目的过程。
一个BS项目最终运行的并不是动态web工程本身,而是这个动态web工程编译的结果。而jdk等并不是目录,只是一个引用。
那么项目构建的全过程中都包含哪些环节呢?
2.3 构建过程
①清理clean:删除以前的编译结果class文件,为重新编译做好准备。
②编译compile:将 Java 源程序编译为字节码class文件。
③测试:针对项目中的关键点进行测试,确保项目在迭代开发过程中关键点的正确性。
④报告:在每一次测试后以标准的格式记录和展示测试结果。
⑤打包package:将一个包含诸多文件的工程封装为一个压缩文件用于安装或部署。 Java 工程对应 jar 包, Web工程对应 war 包。
⑥安装install:在 Maven 环境下特指将打包的结果——jar 包或 war 包安装到本地仓库中。
⑦部署:将打包的结果部署到远程仓库或将 war 包部署到服务器上运行。
在IDEA中有如下显示:
2.4 自动化构建
其实上述环节我们在 Eclipse 中都可以找到对应的操作,只是不太标准。那么既然 IDE 已经可以进行构建了我们为什么还要使用 Maven 这样的构建工具呢?我们来看一个小故事 :
这是阳光明媚的一天。托马斯向往常一样早早的来到了公司,冲好一杯咖啡,进入了自己的邮箱——很 不幸, QA 小组发来了一封邮件,报告了他昨天提交的模块的测试结果——有 BUG。“好吧,反正也不是第一次”, 托马斯摇摇头,进入 IDE,运行自己的程序,编译、打包、部署到服务器上,然后按照邮件中的操作 路径进行测试。“嗯,没错,这个地方确实有问题”,托马斯说道。于是托马斯开始尝试修复这个 BUG,当他 差不多有眉目的时候已经到了午饭时间。 下午继续工作。 BUG 很快被修正了,接着托马斯对模块重新进行了编译、打包、部署,测试之后确认没有问题了,回复了 QA 小组的邮件。 一天就这样过去了,明媚的阳光化作了美丽的晚霞,托马斯却觉得生活并不像晚霞那样美好啊。
让我们来梳理一下托马斯这一天中的工作内容
从中我们发现,托马斯的很大一部分时间花在了“编译、打包、部署、测试”这些程式化的工作上面,而真正需要由“人”的智慧实现的分析问题和编码却只占了很少一部分。
能否将这些程式化的工作交给机器自动完成呢? ——当然可以!这就是自动化构建
此时 Maven 的意义就体现出来了, 它可以自动的从构建过程的起点一直执行到终点
2.5 Maven 核心概念
Maven 能够实现自动化构建是和它的内部原理分不开的, 这里我们从 Maven 的九个核心概念入手,看看 Maven 是如何实现自动化构建的
①POM
②约定的目录结构
③坐标
④依赖管理
⑤仓库管理
⑥生命周期
⑦插件和目标
⑧继承
⑨聚合
3 安装
Maven 的核心程序中仅仅定义了抽象的生命周期,而具体的操作则是由 Maven 的插件来完成的。 可是Maven 的插件并不包含在 Maven 的核心程序中, 在首次使用时需要联网下载。
下载得到的插件会被保存到本地仓库中。 本地仓库默认的位置是: ~.m2\repository。
如果不能联网可以使用我们提供的 RepMaven.zip 解压得到。 具体操作参见“Maven 操作指南.txt”。
安装maven核心程序:
- 检测JAVA_HOME环境变量
- 解压maven核心程序压缩包–apache-maven–版本号
- 配置maven环境变量,配置MAVEB_HOME或M2_HOME(bin目录的上一级),path带bin
- 运行mvn -v查看版本
4 设置
maven的核心程序中仅仅定义了抽象的生命周期,但具体的工作必须由特定的插件来完成。而插件本身并不包含在maven的核心程序中
- 本地仓库:当我们执行的maven命令需要用到某些插件时,maven核心程序会首先到本地仓库(个人电脑)中查找。本地仓库的默认位置:系统中当前用户的家目录如:C:/Users/Admin/.m2/repository/。可以修改本地默认仓库位置:找到maven解压目录==/conf/settings.xml中找到
<localRepository>/path/to/local/repo</localRepository>
,把标签体内容修改为准备好的maven仓库目录。如<localRepository>D:\MavenRepository</localRepository>
== - 中央仓库:maven核心程序如果在本地仓库中找不到需要的插件,那么它会自动连接外网,到中央仓库(云端)下载。如果此时无法联网,那么下载失败。
- 私服仓库:公司自己的仓库,公司的电脑可能禁网只能用局域网的
仓库中的文件:
- [1] Maven 的插件
- [2] 我们自己开发的项目的模块
- [3] 第三方框架或工具的 jar 包
※不管是什么样的 jar 包,在仓库中都是按照坐标生成目录结构,所以可以通过统一的方式查询或依赖。
4 maven项目文件目录
我们在开发中如果需要让第三方工具或框架知道我们自己创建的资源在哪从而进行编译运行,那么基本上就是两种方式:
- ①通过配置的形式明确告诉它
- ②基于第三方工具或框架的约定
Maven 对工程目录结构的要求就属于第二种,下面是Maven的目录。
约定>配置>编码。
pom.xml
其中有个pom.xml文件,就是我们maven项目的配置文件,与构建过程相关的一切设置都在这个文件中配置。重要程序相当于web.xml对于动态web工程
pom(Project Object Model):项目对象模型。将 Java 工程的相关信息封装为对象作为便于操作和管理的模型。
5 常用maven命令:
注意:执行与构建过程相关的maven命令,必须进入pom.xml所在的目录。
mvn compile
: 编译(生成了target/classes目录)mvn test-compile
:编译测试程序(生成了target/test-classes目录)mvn test
:执行测试mvn package
:打包(生成surefire-reports和maven-archiver,打包)生成在target/.war,命名方式是pom中写的a+v.<packaging>
mvc install
:安装mvc site
:生成站点mvn clean
:清理编译后的目录mvc tomcat:run
:在cmd上跑起来项目
6 坐标
数学几何中的坐标:坐标轴xyz确定三维空间一个点
maven的坐标:在中央仓库或本地仓库中确定一个依赖插件是什么(也是一个maven过程)
- [1] groupid:公司或组织的域名倒序 + 当前项目名称
- [2] artifactId:当前项目的模块名称
- [3] version:当前模块的版本
如果是本地仓库的话还可以加载我们自己的项目,如:com/atguigu/maven/Hello/0.0.1-SNAPSHOT/Hello-0.0.1-SNAPSHOT.jar
。
※注意:我们自己的 Maven 工程必须执行安装操作才会进入仓库。 安装的命令是:mvn install
<groupId>com.atguigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<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>
<!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
<groupId>com.companyname.project-group</groupId>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactId>project</artifactId>
<!-- 版本号 -->
<version>1.0</version>
</project>
节点 | 描述 |
---|---|
project | 工程的根标签。 |
modelVersion | 模型版本需要设置为 4.0。 |
groupId | 这是工程组的标识。它在一个组织或者项目中通常是唯一的。例如,一个银行组织 com.companyname.project-group 拥有所有的和银行相关的项目。 |
artifactId | 这是工程的标识。它通常是工程的名称。例如,消费者银行。groupId 和 artifactId 一起定义了 artifact 在仓库中的位置。 |
version | 这是工程的版本号。在 artifact 的仓库中,它用来区分不同的版本。例如:com.company.bank:consumer-banking:1.0 com.company.bank:consumer-banking:1.1 |
常用依赖:
jdk
我在Eclipse中新建Maven项目,但是Maven默认使用的JDK版本是1.5的,但是我想使用1.8版本,所以需要制定版本。这里有两类方法:
①pom.xml文件指定/settings.xml文件指定
<!--pom.xml中指定,制定完右键项目update maven即可-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
②在tomcat的conf/settings.xml中设置
<!--settings.xml中设置-->
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
spring
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
tomcat
去官网搜索
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
mybatis
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
7 依赖dependency
7.1 什么是依赖
A jar包用到了B jar包中的某些类,那么A就对B产生了依赖,在项目中体现用dependency标签加依赖。
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
- maven解析依赖信息时,会到本地仓库中查找被依赖的jar包。对于我们开发的maven工程,我们使用install命令安装后就可以进入仓库(代码就会复制到仓库中)。在被依赖的工程中执行maven install
7.2 依赖的作用域scope
大家注意到上面的依赖信息中除了目标 jar 包的坐标还有一个 scope 设置, 这是依赖的范围。 依赖的范围有几个可选值, 我们用得到的是: compile、 test、 provided 三个。
compile | test | provided | |
---|---|---|---|
主程序(对主程序是否有效) | √ | × | √ |
测试程序(对测试程序是否有效) | √ | √ | √ |
参与部署(是否参与打包) | √ | × | × |
举例: | junit | servlet-api.jar |
对什么有没有效是指:这个依赖提不提供给 主程序或测试程序
[1]从项目结构角度理解 compile 和 test 的区别:
结合具体例子: 对于 HelloFriend 来说, Hello 就是服务于主程序的, junit 是服务于测试程序的。
HelloFriend 主程序需要 Hello 是非常明显的, 测试程序由于要调用主程序所以也需要 Hello, 所以compile 范围依赖对主程序和测试程序都应该有效。
HelloFriend 的测试程序部分需要 junit 也是非常明显的, 而主程序是不需要的,所以 test 范围依赖仅仅对于主程序有效。
[2]从开发和运行这两个不同阶段理解 compile 和 provided 的区别 :
开发的时候需要servlet-API.jar
,但是运行的时候Tomcat会提供,所以就不需要servletAPI.jar
了,所以是provided
7.③依赖的==传递性
A 依赖 B, B 依赖 C, A 能否使用 C 呢? 那要看 B 依赖 C 的范围是不是 compile, 如果是则可用, 否则不可用。
7.④依赖的排除
如果我们在当前工程中引入了一个依赖是 A,而 A 又依赖了 B,那么 Maven 会自动将 A 依赖的 B 引入当前工程,但是个别情况下 B 有可能是一个不稳定版,或对当前工程有不良影响。 这时我们可以在引入 A 的时候将 B 排除。
[1]情景举例
[2]配置方式、
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>HelloFriend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
[3]排除后的效果
7.⑤统一管理所依赖 jar 包的版本
对同一个框架的一组 jar 包最好使用相同的版本。为了方便升级框架,可以将 jar 包的版本信息统一提取出来
[1]统一声明版本号
[2]引用前面声明的版本号
<properties>
<!--自定义标签-->
<atguigu.spring.version>4.1.1.RELEASE</atguigu.spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<!--使用之前定义的标签-->
<version>${atguigu.spring.version}</version>
</dependency>
</dependencies>
[3]其他用法
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties>
7.⑥依赖的原则: 解决 jar 包冲突
[1]路径最短者优先
[2]路径相同时先声明者优先
这里“声明”的先后顺序指的是 dependency 标签配置的先后顺序。
9 maven命令的生命周期
9.1 什么是 Maven 的生命周期?
各个构建环节执行的顺序:不能打乱顺序,必须按照既定的正确顺序来执行。maven的核心程序中定义了抽象的生命周期,生命周期中各个阶段的具体任务是由插件来完成的。
Maven 生命周期定义了各个构建环节的执行顺序,有了这个清单, Maven 就可以自动化的执行构建命令了。
Maven 有三套相互独立的生命周期, 分别是:
- ①Clean声明周期:在进行真正的构建之前进行一些清理工作。clean
- ②Default声明周期:构建的核心部分,编译,测试,打包,安装,部署等等。compile test-compile/test/package/install
- ③Site声明周期: 生成项目报告,站点,发布站点,描述文档静态页。site命令
它们是相互独立的,你可以仅仅调用 clean 来清理工作目录,仅仅调用 site 来生成站点。 当然你也可以直接运行 mvn clean install site 运行所有这三套生命周期。
每套生命周期都由一组阶段(Phase)组成,我们平时在命令行输入的命令总会对应于一个特定的阶段。比如,运行 mvn clean,这个 clean 是 Clean 生命周期的一个阶段。有 Clean 生命周期,也有 clean 阶段。
- 相同生命周期中的命令才会受执行顺序的影响。
- default生命周期中的命令执行顺序:
- compile>>test-compile>>test>>package>>install
顺序依次为 | 处理 | 描述 |
---|---|---|
验证 validate | 验证项目 | 验证项目是否正确且所有必须信息是可用的 |
编译 compile | 执行编译 | 源代码编译在此阶段完成 |
测试 Test | 测试 | 使用适当的单元测试框架(例如JUnit)运行测试。 |
包装 package | 打包 | 创建JAR/WAR包如在 pom.xml 中定义提及的包 |
检查 verify | 检查 | 对集成测试的结果进行检查,以保证质量达标 |
安装 install | 安装 | 安装打包的项目到本地仓库,以供其他项目使用 |
部署 deploy | 部署 | 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程 |
9.2 Clean 生命周期
Clean 生命周期一共包含了三个阶段:
①pre-clean 执行一些需要在 clean 之前完成的工作
②clean 移除所有上一次构建生成的文件
③post-clean 执行一些需要在 clean 之后立刻完成的工作
9.3 Site 生命周期
①pre-site 执行一些需要在生成站点文档之前完成的工作
②site 生成项目的站点文档
③post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
④site-deploy 将生成的站点文档部署到特定的服务器上这里经常用到的是 site 阶段和 site-deploy 阶段,用以生成和发布 Maven 站点,这可是 Maven 相当强大的功能,Manager 比较喜欢,文档及统计数据自动生成,很好看。
9.4 Default 生命周期
maven核心程序为了更好地实现自动化构建,按照这一特点执行生命周期的各个阶段:不论现在要执行生命周期的哪一解读那,都是从这个生命周期最初的位置开始执行。
Default 生命周期是 Maven 生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。这里,只解释一些比较重要和常用的阶段:
validate
generate-sources
process-sources
generate-resources
process-resources 复制并处理资源文件,至目标目录,准备打包。
compile 编译项目的源代码。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources 复制并处理资源文件,至目标测试目录。
test-compile 编译测试源代码。
process-test-classes
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
prepare-package
package 接受编译好的代码,打包成可发布的格式,如 JAR。
pre-integration-test
integration-test
post-integration-test
verify
install 将包安装至本地仓库,以让其它项目依赖。
deploy 将最终的包复制到远程的仓库,以让其它开发人员与项目共享或部署到服务器上运行。
如:
9.5 生命周期与自动化构建
运行任何一个阶段的时候,它前面的所有阶段都会被运行, 例如我们运行 mvn install 的时候,代码会被编译,测试,打包。 这就是 Maven 为什么能够自动执行构建过程的各个环节的原因。此外, Maven 的插件机制是完全依赖 Maven 的生命周期的,因此理解生命周期至关重要。
10 插件和目标
- Maven 的核心仅仅定义了抽象的生命周期,具体的任务都是交由插件完成的。
- ==每个插件都能实现多个功能,每个功能就是一个插件目标。==各个阶段和插件的目标是对应的。
- Maven 的生命周期与插件目标相互绑定,以完成某个具体的构建任务。相似的目标由特定的插件来完成。
例如: compile 就是插件 maven-compiler-plugin 的一个目标; pre-clean 是插件 maven-clean-plugin 的一个目标。
生命周期阶段 | 插件目标 | 插件 |
---|---|---|
compile | compile | maven-compiler-pligin |
test-compile | testCompile | maven-compiler-plugin |
- 生命周期的各个阶段仅仅定义了要执行的任务是什么
- 各个阶段和插件的目标是对应的
- 相似的目标由特定的插件来完成
- 可以将目标看作“调用查件功能的命令”
https://blog.csdn.net/weixin_33775572/article/details/91941836
常用插件:
maven-compiler-plugin:
设置编译项目jdk版本与编码,target版本一定大于等于source版本,编译源文件版本(source)与目标得到的class文件版本(target)可不一致,例如编译需用jdk1.7版本,得到的class需要能通过jdk1.8版本的情况
这个插件就如同名字所显示的这样,用来编译源代码的。最开始碰到这个插件是在于有的时候我们下载了一些工程需要编译的时候,比如我们输入命令:mvn install ,但是系统编译的时候报错了,错误的信息如下:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.0.2:compile (default-compile) on project springJMS: Compilation failure: Compilation failure:
[ERROR] /home/frank/programcode/SpringJMSSample/src/main/java/huangbowen/net/jms/MessageSender.java:[6,1] error: annotations are not supported in -source 1.3
[ERROR]
[ERROR] (use -source 5 or higher to enable annotations)
[ERROR] /home/frank/programcode/SpringJMSSample/src/main/java/net/EmbedBrokerApp.java:[5,7] error: static import declarations are not supported in -source 1.3
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
从错误显示的信息我们就可以看出,这是因为编译的时候是默认用的javac 1.3版本的,太老了不支持代码里的特性。为了修改这个问题,我们需要设置编译器的版本。解决这个问题的办法也比较简单,就是直接在后面的插件部分增加如下的插件,比如如下部分,将编译器的版本设定为1.6:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
maven-jar-plugin:
打jar包,无依赖包。package中指定jar,不单独在plugin中指定,使用默认版本
maven-dependency-plugin:
还有一个比较常用的插件就是这个。我们在IDE的环境里编译和执行代码的时候,那是直接引用一些类库。但是在我们实际部署的环境里,那边很可能就一个java执行环境,不可能有源代码和IDE。这个时候,我们需要将源代码编译打包。这个时候的一个问题就是如果我们引用的库很多的话,我们希望能够把他们统一打包到一个目录下,比如lib文件夹。这样部署执行的时候只需要将编译生成的程序jar包和依赖包文件夹拷到特定目录去执行。要实现这个效果也比较容易:
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
maven-assembly-plugin:
将项目打成可执行的jar包,主要打发布包使用,通过一个xml文件定义更多自定义打包项
maven-shade-plugin:
将项目打成可执行的jar包
assembly与shade的区别:
对xsd文件的处理方式不同,assembly是找到一个放入最终打出的jar包里,但是这样有个问题是当工程依赖到不同版本的依赖包时只能将某一个版本的xsd配置文件放入最终打出的jar包里,这就有可能遗漏了一些版本的xsd的本地映射,会报错;shade插件打包时在对spring.schemas文件处理上,它能够将所有jar里的spring.schemas文件进行合并,在最终生成的单一jar包里,spring.schemas包含了所有出现过的版本的集合
maven-war-plugin:
打war包,有依赖包。package中指定war,不单独在plugin中指定,使用默认版本
maven-surefire-plugin:
Maven通过Maven Surefire Plugin插件执行单元测试。(通过Maven Failsafe Plugin插件执行集成测试),也可用于打包时跳过测试
spring-boot-maven-plugin:
能够将Spring Boot应用打包为可执行的jar或war文件,然后以通常的方式运行Spring Boot应用。否则不管打的是jar包还是war包,都会提示(MANIFEST.MF中)找不到主清单
exec-maven-plugin
我们写一些java console相关的程序时,比较头疼的一点就是真正要通过命令行将打包后的程序执行起来还是比较麻烦的。我们需要在命令行里敲如下的命令:java -cp ***.jar:**.jar:/**/
。因为要将classpath目录以及引用的类库都加入进来,并指定运行的入口,这样子每次用手工的方式来处理实在是太繁琐也比较容易出错。所以一种办法就是利用这个插件,通过一些基本的配置,我们可以执行起代码来的时候很方便。一个典型的配置如下:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.yunzero.App</mainClass>
</configuration>
</plugin>
如果我们运行的时候需要提供一些输入的参数,也可以通过configuration的元素里添加。这样后续要执行这个程序时,我们只需要在命令行执行如下命令:mvn exec:java ,然后程序就可以运行起来了。
maven打jar包的三种方式:
1. 使用maven-jar-plugin和maven-dependency-plugin插件打包
2. 使用maven-assembly-plugin插件打包
3. 使用maven-shade-plugin插件打包
具体使用方法查看参考3
11 继承
11.1为什么需要继承机制?
由于非 compile 范围的依赖信息是不能在“依赖链”中传递的, 所以有需要的工程只能单独配置。 例如
Hello | <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.0</version> <scope>test</scope> </dependency> |
---|---|
HelloFriend | <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.0</version><scope>test</scope> </dependency> |
MakeFriend | <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.0</version> <scope>test</scope> </dependency> |
此时如果项目需要将各个模块的 junit版本统一为 4.9,那么到各个工程中手动修改无疑是非常不可取的。
使用继承机制就可以将这样的依赖信息统一提取到父工程模块中进行统一管理。
11.2创建父工程
创建父工程和创建一般的 Java 工程操作一致,唯一需要注意的是: 打包方式处要设置为 pom
。
11.3在子工程中引用父工程
<parent>
<!-- 父工程坐标 -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<relativePath>从当前目录到父项目的 pom.xml 文件的相对路径</relativePath>
</parent>
举例
<parent>
<groupId>com.atguigu123.maven</groupId>
<artifactId>Parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 指定从当前子工程的pom.xml文件出发,查找父工程的pom.xml的路径 -->
<relativePath>../Parent/pom.xml</relativePath>
</parent>
此时如果子工程的 groupId 和 version 如果和父工程重复则可以删除。
11.4在父工程中管理依赖
将 Parent 项目中的 dependencies 标签,用 dependencyManagement 标签括起来
<build>
<!--插件 -->
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!--端口号 -->
<port>8090</port>
<!--项目访问的根目录 url:localhost:8090/项目名称/addUser.action-->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
在子项目中重新指定需要的依赖,删除范围和版本号
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
12 聚合
12.1为什么要使用聚合?
将多个工程拆分为模块后, 需要手动逐个安装到仓库后依赖才能够生效。 修改源码后也需要逐个手动进行 clean 操作。 而使用了聚合之后就可以批量进行 Maven 工程的安装、清理工作。
12.2如何配置聚合?
在总的聚合工程中使用 modules/module 标签组合, 指定模块工程的相对路径即可
<modules>
<module>../Hello</module>
<module>../HelloFriend</module>
<module>../MakeFriends</module>
</modules>
13 tomcat热部署
热部署:tomcat还在运行就可以把项目部署上线
修改Tomcat的conf/tomcat-users.xml配置文件。添加用户名、密码、权限。
<role rolename="manager-gui" />
<role rolename="manager-script" />
<user username="tomcat" password="tomcat" roles="manager-gui, manager-script"/>
重启tomcat,然后在pom.xml中设置
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<!--
一般eclipse启动项目时候这里配置什么端口,访问项目的时候就是什么端口;用了热部署后,
是部署到目标tomcat里,因此这个port算是没用,访问时,是在tomcat的端口
-->
<port>8081</port>
<!-- 部署到ROOT下 -->
<path>/</path>
<!-- tomcat的地址和端口,manager/text是固定的 -->
<url>http://192.168.70.18:8080/manager/text</url>
<username>tomcat</username>
<password>tomcat</password>
</configuration>
</plugin>
</plugins>
</build>
2、使用maven命令进行部署。
(1)如图所示,命令为clean tomcat7:redeploy,如果是第一次部署,则为deploy,由于pom.xml配置的tomcat插件中的path为反斜杠/,这样就表示部署到tomcat的ROOT项目(ROOT项目肯定是部署过的),因此算是覆盖tomcat自带的ROOT项目,用redeploy重新部署。
(2)记得给Skip Tests 打上勾,跳过测试,或者不打勾,则命令为:
clean tomcat7:redeploy -DskipTests。
14 在Eclipse中使用maven
-
maven插件:Eclipse设置-Prefrences-Maven
-
maven插件的设置:
- Prefrences-Maven-installations-add:指定maven核心程序的位置。不建议使用插件自带的的maven查程序,而应该使用我们自己解压的那个
- Prefrences-Maven-installations-user settings–browse:指定conf/settrings.xml的位置,进而获取本地仓库的位置。
基本操作:
- 创建maven版的java工程:新建–maven projject–选择create a simple project。Next–GAV+packaging:jar
- 创建maven版的web工程:跟上面一样,不过packging:war。在Properties for perfect/
Project Facets
中先取消勾选Dynamic Web Module,暂时将这个勾选取消-应用,骗Eclipse这是一个java工程。然后再选上。勾选Generate web.xml deployment descriptor
。然后他的结构就同时满足了Maven和Eclipse
刚创建的适合,只是在src/main下多了个webapp,他就说这是个maven工程了,显然这是不合理的。我们要的是WebContent/WEB-INF这些
- 执行maven命令:在pom.xml上右键–run as --Maven test等。选
build...
– - Goals:compile
目录 | 目的 |
---|---|
${basedir} | 存放pom.xml和所有的子目录 |
${basedir}/src/main/java | 项目的java源代码 |
${basedir}/src/main/resources | 项目的资源,比如说property文件,springmvc.xml |
${basedir}/src/test/java | 项目的测试类,比如说Junit代码 |
${basedir}/src/test/resources | 测试用的资源 |
${basedir}/src/main/webapp/WEB-INF | web应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面 |
${basedir}/target | 打包输出目录 |
${basedir}/target/classes | 编译输出目录 |
${basedir}/target/test-classes | 测试编译输出目录 |
Test.java | Maven只会自动运行符合该命名规则的测试类 |
~/.m2/repository | Maven默认的本地仓库目录位置 |
想让JSP有提示,需要写入依赖servlet aip
想让使用JSP内置对象有提示,如EL,需要导入jsp-api
导入maven工程:Import-existing Maven Projects,是根据pom.xml来识别工程的
Maven 的 Snapshot 版本与 Release 版本
1、Snapshot 版本代表不稳定、尚处于开发中的版本。
2、Release 版本则代表稳定的版本。
3、什么情况下该用 SNAPSHOT?
协同开发时,如果 A 依赖构件 B,由于 B 会更新,B 应该使用 SNAPSHOT 来标识自己。这种做法的必要性可以反证如下:
- a. 如果 B 不用 SNAPSHOT,而是每次更新后都使用一个稳定的版本,那版本号就会升得太快,每天一升甚至每个小时一升,这就是对版本号的滥用。
- b.如果 B 不用 SNAPSHOT, 但一直使用一个单一的 Release 版本号,那当 B 更新后,A 可能并不会接受到更新。因为 A 所使用的 repository 一般不会频繁更新 release 版本的缓存(即本地 repository),所以B以不换版本号的方式更新后,A在拿B时发现本地已有这个版本,就不会去远程Repository下载最新的 B
4、 不用 Release 版本,在所有地方都用 SNAPSHOT 版本行不行?
不行。正式环境中不得使用 snapshot 版本的库。 比如说,今天你依赖某个 snapshot 版本的第三方库成功构建了自己的应用,明天再构建时可能就会失败,因为今晚第三方可能已经更新了它的 snapshot 库。你再次构建时,Maven 会去远程 repository 下载 snapshot 的最新版本,你构建时用的库就是新的 jar 文件了,这时正确性就很难保证了。
附加内容
一键构建:maven自身继承了tomcat插件,可以对项目进行编译,测试,打包,安装,发布等操作。
仓库的种类:本地仓库,远程仓库(私服),中央仓库
仓库之间的关系:当我们, 启动一个maven工程的适合,maven工程会通过pom文件中的jar包的坐标去本地仓库中找对应的jar包。默认情况下,如果本地仓库没有对应jar包,maven工程自动去中央仓库中下载jar包到本地仓库。;在公司中,如果本地没有对应jar包,会先从私服下载jar包,如果私服没有jar包,可以从中央仓库下载,也可以从本地上传。
maven常用命令:clean,compile,test,package,install,deploy
maven三套生命周期:清理生命周期,默认生命手气,站点生命周期。
点到数据库名上,然后数据库/执行sql语句
parent、dependencies、dependencyManagement区别
假设A作为parent项目,B和C项目依赖于A。
B以parent方式依赖A。
C以dependency方式依赖A。
A中添加了一个类,然后在BC中调用这个类,看看能不能调出来。
测试发现:
- B不能引用项目中的类,所以parent代表不能使用父项目中的类,但可以使用A中的dependency,无需再次定义dependency
- C能引用项目中的类。说明dependency方式可以引用父项目中的类+dependencies
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.7</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
</dependencies>
1.在同一个pom文件下,如果<dependencies>
和<dependencyManagement>
中都对该jar做了依赖,以<dependencies>
的为准,dependencies优先级高于<dependencyManagement>
。若前者没有对其依赖,而后者对其有依赖,则以后者为准。<dependencyManagement>
里只是声明依赖,并不实现引入.
2.在不同的pom文件中,存在父子相互依赖关系的,父项目的pom中<dependencyManagement>
中对该jar做了依赖,而子项目中<dependencies>
又依赖了该jar,
- 如果子项目中没有指定
<version>
和<scope>
,则继承父项目中该jar的<version>
和<scope>
。 - 如果子项目中指定了
<version>
和<scope>
,以子项目的为准。