Maven
一、Maven概述
管理规模庞大的jar包的专门工具
脱离IDE环境执行构建操作的专门工具
构建
构建过程包含的主要的环节:
- 清理:删除上一次构建的结果,为下一次构建做好准备
- 编译: Java源程序编译成*.class字节码文件
- 测试:运行提前准备好的测试程序
- 报告:针对刚才测试的结果生成一个全面的信息打包
- Java工程: jar包
- Web工程:war包
- 安装:把一个Maven工程经过打包操作生成的 jar 包或war 包存入到Maven仓库
- 部署:将准备好的jar包或war包部署到服务器上运行
- 部署jar 包:把一个jar包部署到Nexus私服服务器上
- 部署war 包:借助Maven 插件(例如cargo),将war 包部署到Tomcat服务器上
依赖
如果 A 工程里面用到了 B 工程的类、接口、配置文件等等这样的资源,那么我们就可以说 A 依赖 B。
依赖管理中要解决的具体问题:
- jar包的下载:使用Maven之后, jar包会从规范的远程仓库下载到本地
- jar 包之间的依赖:通过依赖的传递性自动完成
- jar 包之间的冲突:通过对依赖的配置进行调整,让某些jar包不会被导入
Maven的工作机制
二、Maven命令行及配置
D:\Users\zhl36\Documents\apache-maven-3.6.1\conf\setting.xml
中,可以配置 本地仓库,远程镜像仓库 和 Maven工程的JDK编译版本
使用命令生成Maven工程
mvn archetype:generate
mvn: 主命令
archetype:generate:子命令
archetype:插件
generate:目标
Maven核心概念: POM
① POM: Project Object Model ,项目对象模型。模型化思想的具体体现
② 模型化思想
POM表示将工程抽象为一个模型,再用程序中的对象来描述这个模型。这样我们就可以用程序来管理项目了。我们在开发过程中,最基本的做法就是将现实生活中的事物抽象为模型,然后封装模型相关的数据作为一个对象,这样就可以在程序中计算与现实事物相关的数据。
Maven核心概念:约定的目录结构
②约定目录结构的意义
Maven为了让构建过程能够尽可能自动化完成,所以必须约定目录结构的作用。例如:Maven执行编译操作,必须先去 Java源程序目录读取 Java 源代码,然后执行编译,最后把编译结果存放在 target 目录。
③约定大于配置
Maven对于目录结构这个问题,没有采用配置的方式,而是基于约定。这样会让我们在开发过程中非常方便。如果每次创建Maven工程后,还需要针对各个目录的位置进行详细的配置,那肯定非常麻烦。目前开发领域的技术发展趋势就是:约定大于配置,配置大于编码。
三、Maven-web工程
创建Maven版的Web工程
- 操作:回到工作空间的根目录来操作
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.4
- 生成的pom.xml
确认打包方式是war包形式<packaging>war</packaging>
- 生成的Maven工程的目录结构
webapp目录下有index.jsp
WEB-INF目录下有web.xml - 创建Servlet
- ①在main目录下创建java目录
- ②在java目录下创建Servlet类所在的包的目录
- ③在包下创建Servlet类
- ④在web.xml中注册Servlet
<web-app> <servlet> <servlet-name>helloServlet</servlet-name> <servlet-class>com.atguigu.maven.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloServlet</servlet-name> <url-pattern>/helloServlet</url-pattern> </servlet-mapping> </web-app>
- 在index.jsp页面编写超链接
<hjsptml> <body> <h2>Hello World!</h2> <a href="helloServlet">Access Servlet</a> <body> </html>
- 编译
直接编译会报错,在web工程中使用到了HttpServlet这个类,而HttpServlet类属于servlet-api.jar包。说明工程需要依赖servlet-api.jar包 - 配置servle-api.jar包的依赖
可以在mvnreposity.com中寻找依赖的第三方jar包,导入到pom.xml - 将web工程打包为war包
运行mvn package 命令,生成war包的位置如下图所示:
- 将war包部署到Tomcat上运行
将war包复制到Tomcat/webapps 目录下
切换到Tomcat/bin目录下,执行startup.bat 开启Tomcat服务器,在浏览器上访问localhost:8080/webappswar包名/index.jsp 即可
让Web工程依赖Java工程
- 明确一个意识: Web工程依赖java工程。本质上来说, Web工程依赖的Java工程其实就是Web工程里导入的jar包。最终Java工程会变成jar包,放在Web工程的WEB-INF/lib目录下。
- 在 pro02-maven-web工程的pom.xml 中,引入pro01-maven-java 的坐标信息
- 在web工程中,编写测试代码
- 补充创建目录
- 在 web 工程如果缺少test目录,将其目录补充完整,实现约定
- src/test/java/com/atguigu/maven
- 确认web工程依赖了junit
- 创建测试类
- 将Java工程的CalculatorTest.java类复制到pro02-maven-web/src/test/java/com/atguigu/maven目录下
- 执行maven命令
- 测试命令:
mvn test
说明:测试操作会天谴自动执行编译操作,测试成功就说明编译也是成功的。 - 打包命令
mvn package
通过查看war包内的结构,我们看到被Web工程依赖的Java工程确实是会变成Web工程的 target/WEB-INF/lib目录下的jar包 ==》且 jar包只有一个,pro01-maven-java-1.0-SNAPSHOT.jar ,因为maven范围为默认compile,其他两个依赖servlet-api, junit 分别是 provided 和 test - 查看当前Web工程所依赖的jar包的列表
mvn dependency:list - 以树形结构查看当前Web工程的依赖信息
mvn dependency:tree
四、Maven-测试依赖的范围
依赖范围
标签的位置:dependencies/dependency/scope
标签的可选值:compile/test/provided/system/runtime/import
? 默认是compile
compile 和 test 对比
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
---|---|---|---|---|
compile | 有效 | 有效 | 有效 | 有效 |
test | 无效 | 有效 | 有效 | 无效 |
compile 和 provided对比
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
---|---|---|---|---|
compile | 有效 | 有效 | 有效 | 有效 |
provided | 有效 | 有效 | 有效 | 无效 |
provided 一般是服务器里已存在的,package时,不必打包进服务器,否则可能导致jar包冲突
结论
- compile :通常使用的第三方框架的jar 包这样在项目实际运行时真正要用到的jar 包都是以 compile范围进行依赖的。比如SSM框架所需jar 包
- test:测试过程中使用的jar 包,以test 范围依赖进来。比如junit
- provided:在开发过程中需要用到的"服务器上的jar 包" 通常以 provided 范围依赖进来。比如 servlet-api、jsp-api。而这个范围的jar 包之所以不参与部署,不放进war 包,就是避免和服务器上已有的同类jar 包产生冲突,同时减轻服务器的负担。”服务器上已经有了,不用带了“
五、Maven-传递与排除
依赖传递
A ---> B ---> C
如果 C 的范围默认是compile ,则可以传递给A 和 B
如果 C 的范围是 test 或 provided ,则可以传递给 B 不能传递给 A
依赖排除
<dependencies>
<!-- 让web工程依赖java工程 -->
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>pro01-maven-java</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!-- 配置依赖的排除 -->
<exclusions>
<!-- 配置具体排除信息,让commons-logging 不要传递到当前工程 (pro02-maven-web) -->
<exclusion>
<!-- 这里指定坐标时不需指定 version -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
六、Maven-继承和聚合
继承
1.作用
在父工程中统一管理项目中的依赖信息,具体来说是管理依赖信息的版本。
它的背景是:
- 对一个比较大型的项目进行了模块拆分。
- 一个project下面,创建了很多个module。
- 每一个 module 都需要配置自己的依赖信息。
通过在父工程中为整个项目维护依赖信息的组合既保证了整个项目使用规范、准确的jar包;又能够将以往的经验沉淀下来,节约时间和精力。
2.操作
①:创建父工程
创建的过程和前面创建pro01-maven-java一样
工程名称 pro03-maven-parent
工程创建好之后,要修改它的打包方式:
<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,所以需要进入 pro03-maven-parent工程的目录,然后运行mvn archetype:generate 命令来创建模块工程。
父工程
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 当前工程作为父工程,管理其他工程,打包方式必须是pom -->
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--创建我们自定义的属性标签-->
<!-- 标签名:属性名 -->
<!-- 标签纸: 属性值 -->
<!-- 引用方式: ${atguigu.spring.version} -->
<atguigu.spring.version>4.0.0.RELEASE</atguigu.spring.version>
</properties>
<!-- 聚合的配置 -->
<modules>
<module>pro04-maven-module</module>
<module>pro05-maven-module</module>
<module>pro06-maven-module</module>
</modules>
<!-- 在父工程中统一管理依赖信息-->
<!-- 注意:父工程 只是管理 依赖,并不是 明确添加依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<!--通过引用属性表达式设定版本号,这样版本就成了一个动态值 -->
<!--通过属性名解析后才知道具体是什么值-->
<version>${atguigu.spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
子工程
<!-- parent 标签给当前工程配置父工程 -->
<parent>
<!-- 通过指定父工程的坐标找到父工程-->
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<!-- 子工程的 groupId 如果和父工程一样,则可以省略 -->
<!--<groupId>com.atguigu.maven</groupId>-->
<!-- 子工程的 version 如果和父工程一样,则可以省略 -->
<!--<version>1.0-SNAPSHOT</version>-->
<!-- 省略 groupId 和 version 后,子工程自己的坐标可以只保留 artifactId-->
<artifactId>pro04-maven-module</artifactId>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<!-- 对于父工程中已经进行了依赖管理的依赖,子工程引用时,可以不写-->
<!-- <version>4.0.0.RELEASE</version> -->
<!-- 情况 1:确实省略了 version 标签:子工程采纳的就是父工程管理的版本-->
<!-- 情况 2:没有省略了 version 标签:
A:这里配置了 version 和 父工程管理的版本一致,最终还是采纳这个版本。
B:这里配置了 version 但是和父工程管理的版本不一致, 那么当前子工程覆盖父工程管理的版本
并最终采纳。绝大部分下,子工程遵从父工程统一管理的依赖
-->
</dependency>
3.实际意义
提高效率
聚合
<!-- 在总工程中 聚合的配置 -->
<modules>
<module>pro04-maven-module</module>
<module>pro05-maven-module</module>
<module>pro06-maven-module</module>
</modules>
好处
- 一键执行Maven 命令: 很多构建命令都可以在"总工程"中一键执行。
以mvn. install命令为例: Maven要求有父工程时先安装父工程;有依赖的工程时,先安装被依赖的工程。 我们自己考虑这些规则会很麻烦。 但是工程聚合之后,在总工程执行mvn install可以一键完成安装,而且会自工动按照正确的顺序执行。 - 配置聚合之后,各个模块工程会在总工程中展示一个列表,让项目中的各个模块一目了然。
依赖循环问题
A --> B --> C --> A ,那么在执行构建操作时会报下面的错误: 循环引用
[ERROR] [ERROR] The projects in the reactor contain a cyclic reference:
七、Maven-idea中的使用
mvn clean install -Dmaven.test.skip=true
-D 表示后面要附加命令的参数,字母D 和后面的参数是紧挨着的,中间没有任何其他字符
maven.test.skip=true 表示在执行命令的过程中跳过测试
在IDEA 中创建 Maven 的web 工程,先创建一个普通的Maven 工程。
在pom.xml 中设置打包方式为 \
点击菜单栏 File -> Facets -> 中间的web 工程 -> 右上侧 + -> 添加 web.xml文件 -> 在WEB-INF\web.xml 前 添加 至 src\main\webapp 目录下 -> 当前窗口修改web-app 的等级(>=2.5) -> 双击右下侧标变红的Web Resource Directory,确定新建目录 -> apply
在main.java 目录下 -> 创建 .java 文件 -> 在创建完成的 webapp目录下 -> 新建 .jsp 文件
点击Edit Configurations -》点击左上角+ -》添加Tomcat Server -》选择local(本地已下载的) -》点击上边栏Deployment(部署) -》点击中间的+ -》添加 Artifact -》选择以 exploded 结尾的 Artifact -》修改Application contexty(服务器访问的地址) -》 点击上边栏的Server -》 在Application server 中点击Configure -》 选择本地下载的Tomcat server地址 -》在 On 'Update' action 选择 Redeploy -》在 On frame deactivation 选择 Update classes and resources -》apply
点击IDEA 上边栏的 Run 即可
导入模块时 选择 Project Structure import module
八、Maven-生命周期-插件和仓库
生命周期
为了让构建过程自动化完成,Maven设定了三个生命周期,生命周期中的每一个环节对应构建过程中的一个操作。
IDEA中Maven项目的生命周期(Lifecyle)简介_idea maven生命周期以及作用-CSDN博客
插件和目标
① 插件
Maven 的核心程序仅仅负责宏观调度,不做具体工作。具体工作都是由 Maven 插件完成的。例如:编译就是由maven-compile-plugin-3.1.jar 插件来执行的。
② 目标
一个插件可以对应多个目标,而每一个目标都和生命周期中的某一个环节对应。
Default 生命周期中有 compile 和 test-compile 两个和编译相关的环节,这两个环节对应compile 和 test-compile 两个目标,而这两个目标都是由 maven-compiler-plugin-3.1.jar 插件来执行的。
仓库
- 本地仓库:在当前电脑上,为电脑上所有Maven 工程服务
- 远程仓库:需要联网
- 局域网:我们自己搭建的Maven私服,例如使用Nexus 技术。
- Internet
- 中央仓库
- 镜像仓库:内容和中央仓库保持一致,但是能够分担中央的负载,同时让用户能够就近访问提高下载速度,例如:Nexus aliyun
建议:不要中央仓库和阿里云镜像混用,否则jar 包来源不纯,彼此冲突
专门搜索 Maven 依赖信息的网站:https://mvnrepository.com