Maven
学习目标
- 理解Maven的用途
- 掌握Maven的基本操作
- 掌握Maven如何创建Web项目
Maven是什么
面临问题
在学习Maven之前,我们先来看一下我们现在做的项目都有哪些问题。假设你现在做了一个crm的系统,项目中肯定要用到一些jar包,比如说mybatis,log4j,JUnit等,除了这些之外,你有可能用到你的同事开发的其他的东西,比如说别人做了一个财务模块或做了一个结算的模块,你在这里边有可能要用到这些东西。
假如有一天你们的项目中mybatis进行了一个升级,但是它内部使用的JUnit没有升级,你升级以后的mybatis假如要用5.0的JUnit,而你项目中目前用的是4.0的,会不会冲突?必然会出问题!这个时候管理起来会比较麻烦,你需要各种调整。更有甚者,假如同事做的这些东西升级了但又没有通知你,这个时候,就会出现几种严重的问题:
(1)jar包不统一,jar不兼容
(2)工程升级维护过程操作繁琐
除此之外,还会有其它的一系列问题。那么要解决这些问题,就用到了我们今天要讲的Maven了。
Maven简介
官网地址:
https://maven.apache.org/
Maven的本质是一个项目管理工具,将项目开发和管理过程抽象成一个项目对象模型(POM)
Maven是用Java语言编写的。他管理的东西统统以面向对象的形式进行设计,最终他把一个项目看成一个对象,而这个对象叫做POM(project object model),即项目对象模型
我们说一个项目就是一个对象,作为对象的行为、对象的属性都有哪些呢?
Maven说我们需要编写一个pom.xml文件,Maven通过加载这个配置文件就可以知道我们项目的相关信息了!到这里我们知道了Maven离不开一个叫pom.xml的文件。因为这个文件代表就一个项目。
提个问题大家思考,如果我们做8个项目,对应的是1个pom文件,还是8个pom文件?肯定是8个!
那Maven是如何帮我们进行项目资源管理的呢?这就需要用到Maven中的第二个东西:依赖管理。这也是它的第二个核心!
所谓依赖管理就是maven对项目所有依赖资源的一种管理,它和项目之间是一种双向关系,即当我们做项目的时候maven的依赖管理可以帮助你去管理你所需要的其他资源,当其他的项目需要依赖我们项目的时候,maven也会把我们的项目当作一种资源去进行管理,这就是一种双向关系。
那maven的依赖管理它管理的这些资源存在哪儿呢?主要有三个位置:本地仓库,私服,中央仓库
本地仓库顾名思义就是存储在本地的一种资源仓库,如果本地仓库中没有相关资源,可以去私服上获取,私服也是一个资源仓库,只不过不在本地,是一种远程仓库,如果私服上也没有相关资源,可以去中央仓库去获取,中央仓库也是一种远程仓库。
Maven除了帮我们管理项目资源之外还能帮助我们对项目进行构建,管理项目的整个生命周期,当然它的这些功能需要使用一些相关的插件来完成,当然整个生命周期过程中插件是需要配合使用的,单独一个无法完成完整的生命周期。
Maven安装
下载安装包
Maven 是一个基于 Java 的工具,所以要做的第一件事情就是安装 JDK。注意Maven不同版本对JDK的版本也是有一定要求:
Maven 3.3 要求 JDK 1.7 或以上
Maven 3.2 要求 JDK 1.6 或以上
Maven 3.0/3.1 要求 JDK 1.5 或以上
Maven下载地址: https://maven.apache.org/download.cgi
解压安装包
安装路径可自行选择,但是要求路径中不能出现中文或者空格
,本讲义中软件放在了D:\dev\apache-maven-3.8.1
下。
设置 Maven 环境变量
进入环境变量设置界面,配置Path
测试配置效果
进入CMD控制台,输入命令mvn -v
,如果出现下列界面则配置成功
如果出现下述界面,则配置失败,检查环境变量路径即可
仓库介绍
在 Maven 的术语中,仓库是一个位置(place)。
Maven 仓库是项目中依赖的第三方库,这个库所在的位置叫做仓库。
在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。
Maven 仓库能帮助我们管理构件(主要是JAR),它就是放置所有JAR文件(WAR,ZIP,POM等等)的地方。
Maven 仓库有三种类型:
- 本地(local):开发者自己电脑上存储资源的仓库,也可从远程仓库获取资源
- 中央(central):maven团队自身维护的仓库,属于开源的
- 远程(remote):第三方团队维护的仓库
私服介绍
私服:各公司/部门等小范围内存储资源的仓库,私服也可以从中央仓库获取资源
私服的作用:
(1)保存具有版权的资源,包含购买或自主研发的jar
(2)一定范围内共享资源,能做到仅对内不对外开放
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
坐标
我们说maven的仓库里存储了各种各样的资源(jar包),那这些资源我们如何找到它们呢?我们需要知道它们具体的一个位置才能知道如何找到它们,这个就叫坐标
坐标:maven中的坐标用于描述仓库中资源的位置
https://repo1.maven.org/maven2/
那maven中的坐标是如何构成的呢?
maven坐标的主要组成如下:
groupId:定义当前资源隶属组织名称(通常是域名反写,如:org.mybatis;com.itheima)
artifactId:定义当前资源的名称(通常是项目或模块名称,如:crm,sms)
version:定义当前资源的版本号
packaging:定义资源的打包方式,取值一般有如下三种
(1)jar:该资源打成jar包,默认是jar
(2)war:该资源打成war包
(3)pom:该资源是一个父资源(表明使用maven分模块管理),打包时只生成一个pom.xml不生成jar或其他包结构
如果要查询maven某一个资源的坐标,我们通常可以去maven的仓库进行查询,
https://mvnrepository.com/,在该网站中可直接搜索想要的资源,然后就能得到该资源的坐标
输入资源名称进行检索
点击你想要的资源进行查看
选择版本查看坐标
maven坐标的作用:
使用唯一标识,唯一性定义资源位置,通过该标识可以将资源的识别与下载工作交由机器完成。
配置Maven本地仓库位置
找到maven目录下的conf目录,拷贝settings.xml
到C:\Users\自己的用户名\.m2目录下
配置阿里镜像
到C:\Users\自己的用户名\\.m2
目录下,创建settings.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:\repository_ali</localRepository>
<mirrors>
<!--阿里镜像地址-->
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
</mirrors>
<profiles>
<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>
</profiles>
</settings>
阿里云一定要配置,直接连接maven中央仓库的时候,速度会比较慢。
maven的本地仓库文件夹可以任意删除,任意拷贝给其他同学用
maven的jar包如果下载失败了,如果想再次下载,需要手动的删除该jar包所在的文件夹
IDEA中创建Maven项目
新建Maven项目
指定项目名和包名
创建完成之后的项目目录结构
完整的项目目录结构介绍
目录 | 目的 |
---|---|
${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 | 测试编译输出目录 |
${basedir}/pom.xml | 项目核心配置文件 |
pom.xml介绍
每一个Maven项目的根目录下都包含一个pom.xml。
pom.xml都会包含最基础的下列配置信息
<?xml version="1.0" encoding="UTF-8"?>
<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>
<!--groupId:项目的名称,项目名称一般以公司域名的倒序
例如: 西亚斯官网地址:https://www.sias.edu.cn/
groupId为: cn.edu.sias
-->
<groupId>com.iflytek</groupId>
<!--artifactId:模块名称(子项目名称)-->
<artifactId>maven01</artifactId>
<!--Version:模块的版本,snapshot(快照版,没有正式发行)、release(正式发行版本)-->
<version>1.0-SNAPSHOT</version>
</project>
Idea的Maven视图窗口
强大的idea对Maven支持非常友好,在idea的右侧有专属的Maven视图,其中有Maven项目的常用命令和常用插件。
编写测试代码
接下来,我们编写一段JDBC代码来进一步学习Maven知识。
在MySql准备测试表
CREATE TABLE `t_user` (
`uid` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`sex` char(1) DEFAULT NULL,
`phone` varchar(11) DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
添加Jar包
在以往的学习中,我们如果想要编写JDBC代码,我们则需要添下载相关jar包,并拷贝到项目当中。
而在Maven中,我们则只需要添加xml片段,即可让Maven帮助我们去完成Jar包的添加。
- 我们去下面网址中寻找对应的jar包的xml片段
https://mvnrepository.com/
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
- 把查询到的xml片段粘贴到pom.xml中
- 刷新工程,下载相关依赖包,下载完毕之后,观察右侧Maven视图
测试Java代码
package com.iflytek;
import java.sql.*;
public class JdbcDemo01 {
public static void main(String[] args) throws Exception {
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3306/test";
//通过DriverManager获取连接
Connection conn = DriverManager.getConnection(url, "root", "12345678");
// 编写预处理SQL
PreparedStatement ps = conn.prepareStatement("select * from t_user");
// 执行查询
ResultSet rs = ps.executeQuery();
// 遍历查询结果
while(rs.next()){
String name = rs.getString("name");
System.out.println(name);
}
conn.close();
}
}
项目生命周期
Maven有三套相互独立的生命周期,分别是:clean、default、site。
clean主要是清理项目、default是Maven最核心的的构建项目、site是生成项目站点。
每一个大的生命周期又分为很多个阶段。
后面的阶段依赖于前面的阶段,这点有点像Ant的构建依赖。
生命周期本身相互独立,用户可以仅仅调用生命周期的某一个阶段,也就是说用户调用了default周期的任何阶段,并不会触发clean周期以及site周期的任何事情。
三大生命周期蕴含着小小的阶段,我们按顺序看一下:
常用命令如下:
- compile:编译
- clean:清理,将target下的class文件清理
- test: 执行单元测试类,执行src/test/java下的类
- package :将java工程打成jar、war。
- install:安装命令,将工程的jar发布到本地仓库
了解:
(1)clean周期:
pre-clean:准备清理 clean:真正的清理工作 post-clean:执行清理后的一些后续工作
(2)default周期:
validate:验证 initialize:初始化配置 generate-sources:生成源代码编译目录 process-sources:处理项目主资源文件,复制资源文件到outputclasspath generate-resources:生成资源目录 process-resources:处理资源文件 complie:编译源代码 process-classes:处理编译后文件 generate-test-sources:生成测试目录 process-test-sources:处理项目测试资源文件,复制测试资源文件到outputclasspath generate-test-resources:生成测试资源文件 process-test-resources:处理测试资源文件 test-compile:编译测试代码 process-test-classes:处理测试代码 test:单元测试运行测试代码 prepare-package:打包前的准备 package:将编译好的代码打包成为jar或者war或者ear等等 pre-integration-test:准备整体测试 integration-test:整体测试 post-integration-test:为整体测试收尾 verify:验证 install:安装到本地Maven库 deploy:将最终包部署到远程Maven仓库
(3)site周期:
pre-site:准备生成站点 site:生成站点及文档 post-site:站点收尾 site-deploy:将生成的站点发布到服务器上
简单回顾:
- package : 打包,jar包、war包到target目录
- compile : 编译源码,生成classes文件到target目录
- clean : 删除target目录的所有内容
- install : 安装打包之后的文件到本地仓库中,其他工程可以直接引用
- deploy : 部署打包之后的文件到公司服务器中
创建Web项目
在idea中创建Maven版的web项目和Java项目类似。网上有资料介绍可以使用Maven骨架去创建项目,但是创建的项目会缺失部分文件夹,所以不建议使用骨架。(可以自行尝试)
新建Maven项目
创建webapp目录
修改项目打包方式
创建web.xml
注意:
web.xml
需要放在WEB-INF
目录下,不是WEB_INF!!!
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
</web-app>
添加相关依赖
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
创建测试Servlet代码
package com.iflytek;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "Servlet01", urlPatterns = {"/hello"})
public class Servlet01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getOutputStream().println("Hello World!");
}
}
添加tomcat7插件
由于官网没有提供tomcat8插件,所以我们选择tomcat7插件即可
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
<port>9090</port>
</configuration>
</plugin>
</plugins>
</build>
运行项目
直接点击插件,即可运行项目,这种方式比部署到tomcat中要方便很多,在工作中比较常用。
启动日志
浏览器访问
思考:
为什么访问http://localhost:9090/会出现404,而不是熟悉的那只tomcat猫?
快捷启动插件
总结
-
新建Maven项目(可以使用骨架)
-
添加缺失的
webapp/WEB-INF/web.xml
-
webapp文件夹图标需要有小蓝点[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HjPh27A0-1684303534012)(https://imags-1259764475.cos.ap-nanjing.myqcloud.com/img/202305171404440.png)]
-
WEB-INF目录注意是横线,不是下划线
-
web.xml里边不能是空白
-
-
添加Servlet和jsp的依赖
-
新建测试Servlet
-
添加tomcat7的插件
-
运行tomcat7的插件启动测试即可
Maven依赖管理
依赖配置与依赖传递
依赖是指在当前项目中运行所需的jar,依赖配置的格式如下图
依赖传递:
依赖具有传递性,分两种
(1)直接依赖:在当前项目中通过依赖配置建立的依赖关系
(2)间接依赖:被依赖的资源如果依赖其他资源,则表明当前项目间接依赖其他资源
注意:直接依赖和间接依赖其实也是一个相对关系
依赖传递的冲突问题:
在依赖传递过程中产生了冲突,我们有三种优先法则
(1)路径优先:当依赖中出现相同资源时,层级越深,优先级越低,反之则越高
(2)声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖靠后的
(3)特殊优先:当同级配置了相同资源的不同版本时,后配置的覆盖先配置的
可选依赖:
排除依赖:
依赖范围
依赖的jar默认情况可以在任何地方可用,可以通过scope
标签设定其作用范围
这里的范围主要是指以下三种范围
(1)主程序范围有效(src/main目录范围内)
(2)测试程序范围内有效(src/test目录范围内)
(3)是否参与打包(package指令范围内)
此外:scope
标签的取值有四种:compile,test,provided,runtime
这四种取值与范围的对应情况如下:
依赖范围的传递性:
聚合与继承
聚合
随着技术的飞速发展和各类用户对软件的要求越来越高,软件本身也变得越来越复杂,然后软件设计人员开始采用各种方式进行开发,于是就有了我们的分层架构、分模块开发,来提高代码的清晰和重用。针对于这一特性,maven也给予了相应的配置。
我们在开发过程中,创建了2个以上的模块,每个模块都是一个独立的maven project,在开始的时候我们可以独立的编译和测试运行每个模块,但是随着项目的不断变大和复杂化,我们期望能够使用简单的操作来完成编译等工作,这时Maven给出了聚合的配置方式。
所谓聚合,顾名思义,就是把多个模块或项目聚合到一起,我们可以建立一个专门负责聚合工作的Maven 工程。
建立该project的时候,我们要注意以下几点:
1.聚合模块本身也做为一个Maven项目,它必须有自己的POM
2.它的打包方式必须为: pom
3.引入了新的元素:modules—module
4.版本:聚合模块的版本和被聚合模块版本一致
5.相对目录:每个module的值都是一个当前POM的相对目录
6.目录名称:为了方便的快速定位内容,模块所处的目录应当与其artifactId一致(Maven约定而不是硬性要求),总之,模块所处的目录必须和模块所处的目录相一致。
7.习惯约定:为了方便构建,通常将聚合模块放在项目目录层的最顶层,其它聚合模块作为子目录存在。这样当我们打开项目的时候,第一个看到的就是聚合模块的POM
8.聚合模块减少的内容:聚合模块的内容仅仅是一个pom.xml文件,它不包含src/main/Java、src/test/java等目录,因为它只是用来帮助其它模块构建的工具,本身并没有实质的内容。
9.聚合模块和子模块的目录:他们可以是父子类,也可以是平行结构,当然如果使用平行结构,那么聚合模块的POM也需要做出相应的更改。
继承
我们在项目开发的过程中,可能多个模块独立开发,但是多个模块可能依赖相同的元素,比如说每个模块都需要Junit,使用spring的时候,其核心jar也必须都被引入,在编译的时候,maven-compiler-plugin插件也要被引入。这时我们采用继承,就不用在每个子模块分别定义了。
如何配置继承:
1.说到继承肯定是一个父子结构,那么我们在父工程中来创建一个parent project
2.: 作为父模块的POM,其打包类型也必须为POM
3.结构:父模块只是为了帮助我们消除重复,所以它也不需要src/main/java、src/test/java等目录
4.新的元素: , 它是被用在子模块中的
5.元素的属性:: 表示父模块POM的相对路径,在构建的时候,Maven会先根据relativePath检查父POM,如果找不到,再从本地仓库查找
6.relativePath的默认值: …/pom.xml
7.子模块省略groupId和version: 使用了继承的子模块中可以不声明groupId和version, 子模块将隐式的继承父模块的这两个元素
聚合与继承的区别
聚合和继承通常是结合使用的,但是其作用是不同的。聚合是将多个模块的工程汇聚到一起,而继承则是指明某个模块工程要继承另一个模块功能。
分模块开发与设计
工程模块与划分
分层架构
Bean
package org.example.bean;
public class User {
private int uid;
private String name;
private String birthday;
private String sex;
private String phone;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Dao
package org.example.dao;
import org.example.bean.User;
public class UserDao {
public User findById(int uid) {
// 查询数据库
return new User();
}
}
Service
package org.example.service;
import org.example.bean.User;
import org.example.dao.UserDao;
public class UserService {
UserDao userDao;
public User findById(int uid) {
// 调用Dao的方法
return userDao.findById(uid);
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
Controller
package org.example.controller;
import org.example.bean.User;
import org.example.service.UserService;
public class UserController {
UserService userService;
public User findById(int uid) {
// 调用Dao的方法
return userService.findById(uid);
}
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
父项目的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>org.example</groupId>
<artifactId>ssm_02</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>ssm_bean</module>
<module>ssm_dao</module>
<module>ssm_service</module>
<module>ssm_controller</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
属性
属性类别:自定义属性
- 作用: 抽取变量,统一维护