Maven
Maven的优势?
1 依赖管理【jar包管理】
传统的jar包很容易造成大量的冗余【如a工程的jar包b工程也要用】,而且jar包之间可能会有依赖关系,需要找到所有的jar包。找jar不方便,也可能不安全。
maven可以解决这些问题。maven可以帮我们直接到统一的官方仓库中直接找到jar包。每个服务器上都有一个本地库,用来存放从官方库下载的jar包。这个服务器上的任意项目如果要用jar包,从本地库引用就可以了。同一台服务器上共用一份jar包。而且maven可以发现jar包的依赖关系,将依赖的包也下载下来。
2 项目管理
可以将一个项目分成多个模块,每个模块可以分布到不同的服务器上。如何实现各个模块直接的管理调用,分布式部署。Maven可以搞定。
3 项目的目录结构
约定大于配置,配置大于编码。Maven的项目结构是统一的。
maven是什么?
他是一款自动化构建【build】工具。
maven的核心?
Maven之所以能够实现自动化的构建,和它的设计是紧密相关的。我们对Maven的学习围绕它的九个核心概念展开。
① 仓库管理
② POM
③ 约定的目录结构
④ 坐标
⑤ 依赖管理
⑥ 生命周期
⑦ 插件和目标
⑧ 继承
⑨ 聚合
Maven构建的主要环节
构建过程包含的主要的环节:
①清理:删除以前的编译结果,为重新编译做好准备。
②编译:将Java源程序编译为字节码文件。
③测试:针对项目中的关键点进行测试,确保项目在迭代开发过程中关键点的正确性。
④报告:在每一次测试后以标准的格式记录和展示测试结果。
⑤打包:将一个包含诸多文件的工程封装为一个压缩文件用于安装或部署。Java工程对应jar包,Web工程对应war包。
⑥安装:在Maven环境下特指将打包的结果——jar包或war包安装到本地仓库中。
⑦部署:将打包的结果部署到远程仓库或将war包部署到服务器上运行。
仓库repository
仓库里有什么
放构件(artifact),包括以下三种
- 第三方框架或工具的jar包
- Maven的插件plugin
- 我们自己开发的项目模块
仓库分类
- 本地仓库
- 远程仓库
- 中央仓库【默认】mvnrepository.com/
- 镜像仓库【为了减轻中央仓库负担,更快响应用户请求,这个仓库就是把中央仓库所有文件复制了一份存起来,如阿里Maven镜像】
- 私服【私服代理广域网上的远程仓库,供局域网内的用户使用】
访问仓库的步骤
Maven根据坐标寻找构件的时候,它会首先查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库没有,或者要查看是否有更新版本,就去访问远程仓库【去找私服,如果没有私服,就去找中央仓库】
Maven安装
1.检查JAVA_HOME是否正确
2.下载安装包
3.解压【安装路径不要有中文、空格等特殊字符】
bin:mvn(maven) mvn的执行文件
conf:settings.xml Maven的配置文件
4.配置环境变量
Java:JAVA_HOME PATH
Tomcat:CATALINA_HOME PATH
Maven : M2_HOME(低版本MAVEN_HOME) Maven主目录 PATH 可执行文件的路径bin
5.验证安装是否正确
C:\Users\Administrator>mvn -v
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: C:\Java\apache-maven-3.6.3\bin\..
Java version: 1.8.0_161, vendor: Oracle Corporation, runtime: C:\Java\jdk1.8.0_161\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
C:\Users\Administrator>mvn -version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: C:\Java\apache-maven-3.6.3\bin\..
Java version: 1.8.0_161, vendor: Oracle Corporation, runtime: C:\Java\jdk1.8.0_161\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
注意:设置了环境变量,要重新启动cmd
6.指定本地仓库【可选】
本地仓库必须有,但是有默认位置:用户家目录/.m2/repository。
conf/settings.xml可以指定额外的位置
<localRepository>D:/repository</localRepository>
7.配置阿里云提供的镜像仓库【可选】
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
8.配置Maven工程的基础JDK版本【可选】
该配置对当前服务器的所有的Maven工程有效,这是一个全局配置。
此处不配置,默认JDK编译版本是JDK5,但是可以在每个工程进行单独配置,局部的配置
<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>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
</profile>
maven目录结构
Java项目的类型
Maven的项目类型有多种,和我们开发相关的项目类型主要有三种:
-
Java项目:被打包为jar包。
-
web项目:打包为war包
-
pom项目:pom项目中不写Java代码, 用来做父项目,用于进行子项目的管理
其中Java项目是默认项目类型,在pom.xml中通过
<packaging>来标记项目类型。这里先创建java项目。
maven命令
clean在一个生命周期中
compile test-compile test package install在一个生命周期中
执行同一个生命周期的后一个操作,会从第一个操作开始依次全自动执行。
| 命令 | 作用 |
|---|---|
| mvn compile | 只编译主代码,放入target目录 |
| mvn clean | 删除target目录 |
| mvn test-compile | 编译主代码和测试代码,放入target目录(resouces也会拷入target目录) |
| mvn test | 编译主代码和测试代码,放入target目录(resouces也会拷入target目录),并且运行测试用例 |
| mvn package | 编译主代码和测试代码,放入target目录(resouces也会拷入target目录),并且运行测试用例,将当前项目打包成一个jar |
| mvn install | 完成mvn package的所有功能,并且将当前jar上传到本地库(其他项目也可以使用) |
| mvn deploy | 在install基础上,将当前的jar上传到远程库(需要更多的条件) |
IDEA继承Maven
我们安装了Maven之后需要在idea中将本地的Maven集成进来。

idea内嵌的Maven的支持,我们不使用它的内嵌版本,而是绑定我们刚刚安装的版本。需要指定Maven的安装目录、配置文件路径、本地仓库的路径。

优化配置:-DarchetypeCatalog=internal:每次创建项目时,IDEA要使用插件进行创建,这些插件当创建新项目时,它每次都会去中央仓库下载,这样使得创建比较慢。应该在创建时让它找本地仓库中插件进行创建项目。注意:-D<property>=<value>,-D代表要添加或覆盖属性。

在IDEA中创建Maven Java工程

指定项目的坐标:
- GroupId:当前项目所属组织ID。一般采用分级结构,域名倒写。
- ArtifactId:构件ID。当前项目的ID。
- Version:当前项目的版本。SNAPSHOT是快照的意思,意为开发版;RELEASE 代表稳定版。
以上三项构成当前项目的唯一坐标。

如果弹出Maven project need to be import,是提示Maven项目需要导入变化,选择Import Changes即可。一般不选择Enable Auto-Import。

pom.xml中添加JUnit、lombok依赖
<?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>com.atguigu.maven</groupId>
<artifactId>_01_maven_java</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Lombok
什么是Lombok
以前的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些样板代码既没有技术含量,又影响着代码的美观,Lombok应运而生。
Lombok 是一种 Java™ 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO)。它通过注解实现这一目的。
| @Data | 集成了@Getter和@Setter、@ToString、@EqualsAndHashCode、@RequiredArgsConstructor这几个注解与一身,是一个非常强大的注解,这个注解更可以简化注解的代码量 |
| @getter | 在JavaBean中使用,注解会生成对应的getter方法 |
| @setter | 在JavaBean中使用,注解会生成对应的setter方法 |
| @NoArgsConstructor | 在JavaBean中使用,注解会生成对应的无参构造方法 |
| @AllArgsConstructor | 在JavaBean中使用,注解会生成对应的有参构造方法 |
| @ToString | 在JavaBean中使用,注解会自动重写对应的toStirng方法 |
| @ToString(exclude={"column1","column2"}) | 排除多个column列所对应的元素 |
| @ToString(of={"column1","column2"}) | 只生成包含多个column列所对应的元素 |
| @EqualsAndHashCode | 在JavaBean中使用,注解会自动重写对应的equals方法和hashCode方法 |
| @Slf4j | 在需要打印日志的类中使用,项目中使用slf4j日志框架 |
| @Log4j | 在需要打印日志的类中使用,项目中使用log4j日志框架 |
如何在idea中使用lombok
第一步:导入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
第二步:添加插件

第三步:进行配置

第四步:使用lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer userId; //Integer 0 null int null
private String userName;
private Integer age;//阿里开发规范中规定,不使用基本数据类型作为成员变量的类型
private String address;
}
使用Maven创建Java Web
方法1【重要】
-
创建Maven的Java项目
-
pom.xml指定类型为war包
<packaging>war</packaging> -
添加web支持

- 指定web.xml的位置

5. 指定web项目的根目录

-
添加web资源
-
静态网页
-
Thymeleaf模板文件
-
图片
注意:
- WEB-INF/下的内容无法在客户端直接输入网址来访问,必须间接访问。先访问服务器端的一个可以访问的资源,然后再转发(不可以是重定向,重定向其实是客户端的访问)到WEB-INF下的资源。
- Thyemeleaf其中的动态标签<th>必须经过Thymeleaf解析器解析后才可以看到最终的结果,如果直接访问,动态标签<th>没有进行处理。所以在项目中一般把thymeleaf也放入到WEB-INF/tempates下。
-
-
部署到服务器并进行访问
-
配置tomcat
-
增加新的Tomcat
-
服务器配置
-
部署配置
-
运行tomcat
-
访问资源:
- 静态网页 :可以直接访问
- Thymeleaf模板文件:即使没有放在WEB-INF下,也不要直接访问
- 图片:可以直接访问
- WEB-INF/ 不可以访问
-
解决控制台乱码
-
点击Help => Edit custom VM Options,在最后面添加 “-Dfile.encoding=UTF-8”
-
点击右上角 Edit Configurations,在tomcat配置中的 VM option中添加 “-Dfile.encoding=UTF-8”
在第二步的Startup/Connection页签的Run和Debug添加一个key为JAVA_TOOL_OPTIONS, value为“-Dfile.encoding=UTF-8”
的环境变量
-
保存后重启idea,可以发现控制台中文乱码显示正常了
如果还不行,那要放大招了(其实可以直接放大招的):
其实出现乱码的是Tomcat的日志乱码,到tomcat8的conf/logging.properties,找到一行java.util.logging.ConsoleHandler.encoding = UTF。将其编码类型修改为另外一个即可。
-
-
方法2【需要下载插件】
- 下载JBLJavaToWeb插件【如果有就不用了】
打开setting

2. 创建普通java maven项目
-
右键选中项目模块,点击“JBLJavaToWeb”

4. 结果如图所示,增加了Web项目的目录支持,并在pom.xml中添加<packaging>war</packaging>
方法3【使用骨架,有不足】
- 选择Maven的项目类型
- 输入坐标
- 确认配置

- 保存项目
注意:此时项目的类型为war类型,而不再是jar类型了。
项目结构是
可以发现,不是标准项目结构,需要手动解决。
- 手动解决项目问题
- main目录下没有java、resources目录
- 没有test目录
- web.xml中dtd版本低(将无法解析EL)
- pom.xml中内容可以精简,build内容可以删除
- pom.xml中没有Servlet、JSP依赖
手动解决这些问题吧,记得将java、resouces目录标记为相应的Root目录。
坐标
Maven的坐标
Maven的坐标元素包括groupId、artifactId、version、packaging、classfier等,
其中groupId、artifactId、version是必须定义的,即可唯一的标记一个坐标,称为坐标三要素,简称GAV。
[1]groupId:公司或组织的域名倒序+当前项目名称
[2]artifactId:当前项目的模块名称
[3]version:当前模块的版本
如
<groupId>com.atguigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
坐标和仓库中jar包的存储路径之间的对应关系
以当前定义的Java项目的坐标为例说明对应关系。
<groupId>com.atguigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
[1]将gav三个向量连起来
com.atguigu.maven+Hello+0.0.1-SNAPSHOT
[2]以连起来的字符串作为目录结构到仓库中查找。jar的名称是artifactId-version.jar
com/atguigu/maven/Hello/0.0.1-SNAPSHOT/Hello-0.0.1-SNAPSHOT.jar
※注意:我们自己的Maven工程必须执行安装操作才会进入仓库。安装的命令是:mvn clean install
使用tomcat7-maven-plugin运行web项目
-
在pom.xml中添加配置信息。
<build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <!--配置tomcat启动的端口号--> <port>8080</port> <!--配置项目的上下文路径--> <path>/mvnweb</path> </configuration> </plugin> </plugins> </build> -
在Maven窗口中选择tomcat7插件,利用其中的tomcat7:run来启动服务器。
此处的tomcat7只是为了和正在使用的tomcat8.5区分开来,正式开发中尽量不要使用tomcat7来开发,它有很多不足。
Maven生命周期
Maven有三套相互独立的生命周期,分别是:
①Clean Lifecycle 在进行真正的构建之前进行一些清理工作。
②Default Lifecycle构件的核心部分,编译,测试,打包,安装,部署等等。
③Site Lifecycle生成项目报个,站点,发布站点。
前面三个生命周期是彼此独立的。在任何一个生命周期内部,执行任何一个具体环节的操作,都是从本周期最初的位置开始执行,直到指定的地方。
但是注意:执行install命令时,会执行default生命周期的前面的所有阶段。但是不会执行clean阶段,因为不是同一套生命周期。
Maven之所以这么设计其实就是为了提高构建过程的自动化程度:让使用者最关心最终要干的事情即可,过程中的各个环节是自动执行的。
插件
Maven的核心仅仅定义了抽象的生命周期,具体的任务都是交由插件完成的。
每个生命周期中都包含着一系列的阶段。这些阶段就相当于Maven提供的统一的接口,然后这些阶段的实现由Maven的插件来完成。
每个插件都能实现多个功能,每个功能就是一个插件目标。
我们在输入mvn命令的时候,比如mvn clean,clean对应的就是clean生命周期中的clean阶段。clean的具体操作就是由maven-clean-plugin来实现的。
所以说Maven生命周期的每一个阶段的具体实现都是由Maven插件实现的。Maven实际上是一个依赖插件执行的框架,每个任务实际上都是由插件完成的。
Maven插件列表的官网页面:maven.apache.org/plugins/
Maven插件的下载位置:本地仓库:\org\apache\maven\plugins
可以将该目录下所有插件全部删掉,然后一次执行各个命令,可以清楚的看到Maven远程仓库下载对应插件的过程。
依赖传递
依赖和范围
| 依赖范围 | 编译(main) | 测试(test) | 运行 | 示例 |
|---|---|---|---|---|
| compile | √ | √ | √ | log4j |
| provided | √ | √ | × | servlet-api lombok |
| test | × | √ | × | junit |
| runtime | × | √ | √ | JDBC驱动类 |
| system | √ | √ | × | |
| import | × | × | × |
如何建立两个项目之间的依赖关系:moduleB依赖于moduleC
<dependencies>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03_moduleC</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
C的资源,B可以使用哪些?

依赖冲突
路径最短者优先,路径相同时先声明者优先。
依赖排除
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03_moduleB</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
排除jar时,不需要写version.
统一管理依赖版本
<properties>
<spring-version>5.2.5.RELEASE</spring-version>
<moduleB-version>1.0-SNAPSHOT</moduleB-version>
<moduleD-version>1.0-SNAPSHOT</moduleD-version>
</properties>
好处:统一的管理依赖的版本,便于对版本的修改。
继承
问题:有了依赖,为什么还要有继承
因为:依赖,只能使用compile的依赖,test、provided无法使用。
特点
- 父项目必须是pom类型
- 父项目本身不提供新的源码功能,src目录可以删除
- 子项目可以从父项目中继承test、compile、provided依赖
- 父项目建议发布到本地库
- 缺点:父项目子项目是父子关系,但是在其实是并列的项目,需要分别构建。
问题:父项目的全部资源会自动的被子项目继承。但是不同的子项目可能只需要其中的一部分。怎么办:
解决:dependencyManagement
父项目:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</dependencyManagement>
子项目:需要就引入,不引人就无法使用;如果版本一致,引入是省略版本号。
好处:便于以后的版本修改,只要修改父项目的版本即可。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.9</version>
</dependency>
</dependencies>
聚合
问题:有了继承,为什么还需要聚合
父项目子项目是父子关系,但是在其实是并列的项目,需要分别构建。
聚合关系是指多个项目共同组成一个项目。 用一个“总工程”将各个“模块工程”汇集起来,作为一个整体对应完整的项目。对外看起来是一个项目。
对父项目进行构建,会自动的对所有的子项目构建。
聚合关系首先是一种继承关系。
父项目:
<groupId>com.atguigu.maven</groupId>
<artifactId>pro05_shopping</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>order</module>
<module>product</module>
<module>payment</module>
</modules>
子项目
<parent>
<artifactId>pro05_shopping</artifactId>
<groupId>com.atguigu.maven</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order</artifactId>
子项目的坐标可以省略groupid和version
对父项目进行构建,会对所有的子项目进行构建。
如果对一个子项目进行构建,不会影响其他子项目。
Jar下载失败的问题分析
正常下载的情况
jar包在下载过程中,jar包的扩展名是xxx.jar.lastUpdated。jar包下载成功后,Maven会将lastUpdated扩展名删除,让jar包恢复为xxx.jar这样的名称
下载失败的第一种情况
1、Maven的行为描述
jar包在下载过程中,jar包的扩展名是xxx.jar.lastUpdated
网络连接丢失,无法继续下载
jar包没有下载完,lastUpdated扩展名不会被删除
当我们要求Maven重新下载时,Maven看到这个jar包的扩展名是lastUpdated,Maven就不管了
2、解决办法
手动删除所有以lastUpdated结尾的文件,然后让Maven重新下载。可是当仓库中lastUpdated文件太多的时候,手动删除不可行,所以我们需要借助批处理文件:clearLastUpdated.bat。使用方法:
- 将clearLastUpdated.bat文件复制到本地仓库的根目录
- 使用文件编辑打开clearLastUpdated.bat文件
- 修改下面的内容
SET CLEAR_PATH=设置为本地仓库所在的盘符 SET CLEAR_DIR=设置为本地仓库的根目录的路径
例如:
SET CLEAR_PATH=D: SET CLEAR_DIR=D:\maven-rep1026
- 在想要执行清理时,双击运行这个文件即可
然后要求Maven重新下载jar包。
失败的第二种情况
1、文件校验工具
jar包表面上看起来是下载完的状态,结尾并没有“.lastUpdated”扩展名。但是程序运行时找不到这个jar包中的类。例如:针对jar包中的类出现ClassNotFoundException。
遇到类似问题需要先确认一下“有嫌疑”的jar包是否存在内部损坏问题。此时可以使用上面的文件校验工具。
2、用法
- 打开文件校验工具
- 将待校验的jar包拖拽到文件校验工具窗口内
- 将待校验的jar包旁边的*.sha1文件打开
- 比较两个SHA1值
- 一致:jar包完好
- 不一致:jar包损坏
3、原理
HASH加密包括很多具体加密算法,但是所有HASH算法都满足下面特征:
- 只要输入数据不变,任何时候执行加密,输出不变
- 输入数据有细微变化,输出数据跟着变化
- 不可逆,不能通过密文反推回明文
- 每一种具体的HASH加密算法加密后长度固定
4、找到有嫌疑的jar包
假设现在出现了一个ClassNotFoundException,提示找不到类:org.springframework.expression.Expression。此时我们怀疑这个类所在的jar包下载失败。
- 窍门1:类所在的package命名往往和jar包坐标的groupId部分类似。
- 窍门2:在IDEA中按两下Shift键,使用全类名搜索
为什么不建议删整个库
1. 整个库删除后,需要重新下载整个库。此时东边的jar包没有失败,可能西边的jar包会失败。
2. 整个库整体重新下载时,会有很多jar包并行下载的,相对来说并行下载本身就容易丢失数据。
