Maven的基本了解
什么是Maven?
Maven就是Apache下的一个开源项目。它是用纯java开发的。是一个项目管理工具。使用Maven对java项目进行构建、依赖管理。
项目构建:
项目构建包括代码编写、编译、测试、运行、打包、部署和运行这几个过程。传统的项目构建需要导入jar包来管理。对于Maven来说,是通过pom.xml配置文件来配置所需要的依赖。
依赖管理:
一个java项目可能要使用一些第三方jar包才可以运行,那么我们的jar包依赖于第三方的jar包。所谓的依赖管理就是对项目所有依赖的jar包进行规范化管理。
Maven的好处
- Maven的项目体积小(体现在,jar包都不会保存在项目中,而是通过pom配置文件查询本地仓库中的jar包)
- 可以一键构建项目
注意:jar包虽然是保存在本地仓库中,但是并不会影响项目的打包
Maven的安装和环境变量的配置
Maven下载地址:maven官网
直接解压在本地中即可
1、MAVEN_HOME的配置
例如我的安装目录是:M:\SOFTWART\apache-maven-3.5.2
因为我方便移动,设置的是临时变量。建议在用户的全局变量中设置。
set path=M:\SOFTWART\apache-maven-3.5.2
2、配置本地仓库 - 也就是配置主键的存放位置,主键可以理解为依赖包
两种方式:
- 用户settings.xml配置文件中设置(用户目录/.m2中配置)
- 全局settings.xml配置文件中设置(Maven目录/conf/setting.xml中配置)
建议采用第一种方式,好处是:第一种方式对其他用户不影响,而且在Maven升级的时候需用修改用户配置文件,而全局配置文件却要修改。
找到settings
根标签,写上关联的本地仓库位置
<localRepository>M:\DOCMENT\repository</localRepository>
Maven命令和生命周期
Maven的命令操作
clean:清理编译的文件
compile:编译主目录代码
test:编译测试代码,并运行代码
package:打包(打包的文件名和类型取决于pom.xml)
install:就是把项目打包到本地仓库目录
tomcat:run: 一键启动,Maven中tomcat默认版本是6,如果要使用其他版本(如:tomcat7:run)
Maven的生命周期
生命周期:为了对所有构件过程进行抽象和统一。
这个生命周期包含了几个过程:清理、初始化、编译、测试、打包、集成测试、验证、部署、站点生成
Maven拥有三套相互独立的生命周期:clean生命周期、default生命周期和site生命周期
clean生命周期
clean生命周期的目的是清理项目
包含三个阶段:
-per-clean:执行一些清理前需要完成的工作
- clean:清理上一次构建生成的文件
- post-clean:执行一些清理后需要完成的工作。
default生命周期
default生命周期的目的是构建项目
核心阶段:(略过其他的)
- process-source:处理项目主资源文件。对src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中。
- compile:编译项目的主源码,编译src/main/java目录下的java文件至classpath目录中。
- process-test-source:处理项目测试资源文件。对src/test/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中。
- test-compile:编译项目的测试代码,编译src/test/java目录下的java文件至classpath目录中。
- test:使用单元测试框架进行运行测试,测试代码不会被打包或部署
- package:接收编译好的代码,打包成可发布的格式,如jar
- install:将包安装到Maven本地仓库,供本地其他Maven项目使用
- deploy:将最终的包复制到远程仓库,供其他开发人员和Maven项目使用
常见的打包方式:jar、war、pom、maven-plugin、ear
site生命周期
site生命周期的目的是建立项目站点
- pre-site:执行一些在生成项目站点之前需要完成的工作
- site:生成项目站点文档
- post-site:执行一些在生成项目站点之后需要完成的工作
- site-deploy:将生成的项目站点发布到服务器上。
不同的生命周期可以同时使用:
mvn clean install
mvn clean deploy site
mvn clean deploy site
命令的执行阶段:执行clean生命周期的clean阶段(包括per-clean、clean)、default生命周期的deploy阶段(包含default生周期的全部阶段)以及site生命周期的site-deploy阶段(site生命周期的全部阶段)
Maven仓库
如果用过git的朋友应该知道,有本地的git仓库,也有远程的git仓库。对于Maven也是一样的。
构件
构件:任何一个依赖、插件或者 项目构建的输出(项目打包后输出的文件)都可以称为构件
任何一个构件都有一组坐标唯一标识。
构件在项目中的应用
了解Maven中处理仓库布局的源码,来了解Maven是如何通过坐标来表示仓库的路径
说明:
仓库路劲为:log4j/log4j/1.2.15/log4j-1.2.15.jar (groupId/artifactId/version/artifactId-version.packageing)
坐标 =>
groupId=log4j
artifactId=log4j
version=1.2.15
packageing=jar
源码解析:
private static final char PATH_SEPARATOR = '/';
private static final char GROUP_SEPARATOR = '.';
private static final char ARTIFACT_SEPARATOR = '-';
public String pathOf(Artifact artifact) {
ArtifactHandler artifactHandler = artifact.getArtifactHandler();
StringBuilder path = new StringBuilder(128);
path.append(formatAsDiretory(artifact.getGroupId())).append(PATH_SEPARATOR);
path.append(artifact.getArtifactId()).append(PATH_SEPARATOR);
path.append(artifact.getBaseVersion()).append(PATH_SEPARATOR);
path.append(artifact.getArtifactId()).append(ARTIFACT_SEPARATOR).append(artifact.getVersion());
if (artifact.hasClassifier()) {
path.append(ARTIFACT_SEPARATOR).append(artifact.getClassifier());
}
if (artifactHandler.getExtension() != null && artifactHandler.getExtension().length() >0 ) {
path.append(GROUP_SEPARATOR).append(artifactHandler.getExtension());
}
return path.toString();
}
private String formatAsDiretory (String directory) {
return directory.replace(GROUP_SEPARATOR, PATH_SEPARATOR);
}
=> artifact.getBaseVersion()主要是为SNAPSHOP版本服务的,例如version为1.0-SNAPSHOP的构件,其baseVersion就是1.0
=> classifier表示的是构建输出的一些附属构建,例如:javadoc、source
Maven仓库种类
Maven仓库分为三种:
- 本地仓库(自己维护)
- 远程仓库
- 私服(公司维护)
- 中央仓库(Maven团队维护)
- 其他公共库(其他人维护)
常见的远程仓库有:
JBoss Maven库
添加中央仓库镜像 -- 阿里云镜像
1、在settings.xml中配置
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
2、在pom.xml中配置
<repositories>
<repository>
<id>maven-ali</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url
> <releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
</repository>
</repositories>
updatePolicy表示用来配置Maven从远程仓库检查更新的频率,默认值是daily
- never:从不检查更新
- always:每次构建都检查更新
- interval:X :每隔X分钟检查一次更新
- daily:每天检查一次
checksumPolicy:用来配置Maven检查检验和文件的策略,当构建部署到Maven仓库的时候,会同时部署对应的校验和文件。 - fail:Maven遇到检验和错误就让构建失败
- ignore:使Maven完全忽略校验和错误
私服
代表软件:Nexus
使用私服的好处:
- 节省字节的外网带宽
- 加速Maven构建
- 部署第三方构件
- 提高稳定性,增强控制(不会受网络的影响、权限管理、RELEASE和SNAPSHOT区分等)
- 降低中央仓库的负荷
配置私服镜像:
<mirror>
<id>mymaven</id>
<name>nexus respository</name>
<url>http://localhost:8081/nexus/content/groups/public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
mirrorOf
-
*
:匹配所有远程仓库 -
external: *
:匹配所有远程仓库,使用localhost的除外,使用file://协议的除外。也就是说匹配所有不在本机上的远程仓库 -
repo1、repo2
:匹配仓库repo1和repo2,使用, 来分割多个远程仓库 -
*,!repo1
:匹配所有的远程仓库,repo1除外
远程仓库的验证以及部署配置
以私服来配置:
仓库验证
<server>
<id>releases</id>
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>snapshots</id>
<username>admin</username>
<password>admin123</password>
</server>
部署到远程仓库配置
<distributionManagement>
<repository>
<id>releases</id>
<url>http://localhost:8081/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<url>http://localhost:8081/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
distributionManagement包含repository和snapshotRepository
- repository:发布版本构建的仓库
- snapshotRepository:快照保本的仓库
详解pom.xml
了解版本 -- REALISE、SNAPSHOT
REALISE版本
REALISE版本是用来表示一个稳定的版本
eg:1.0.0、1.3-alpha-4、2.0
如果要更新到远程仓库里面,需要更改版本号来更新最新的版本。获取最新的版本也需要通过最新的版本号来标识
SNAPSHOT版本
SNAPSHOT版本是在标识一个不稳定的版本。可以用来指定开发过程中的一个版本的快照
eg:2.1-SNAPSHOT、2.1-20111211.221323-11(快照为2.1版本,与2011年12月11日22点12分23秒第11次修订)
快照更新到远程仓库,在发布的过程中,Maven会自动为构建打上时间戳。这样在仓库中就能找到该构建2.1-SNAPSHOT的最新文件。
通常在配置文件中会设置Maven是多久检查更新构件,默认是一天检查更新构件是否为最新构件。在同一个版本的发布,建议使用以下命令来更新版本
mvn clean install-U
了解坐标
Maven有个很大的优势就在于管理项目依赖。为了能自动地解析任何一个Java构件,Maven就必须将它们唯一标识,这就是依赖管理的底层基础 --- 坐标
坐标能够快速、准确的定位到某个构件(jar、war文件),通过设置中央仓库的地址,可以快速的从该中央仓库下载我们需要的一些主键。
坐标的详解:(*表示必填)
-
groupId
*:定义该模块隶属于哪个公司的项目,表示方式和java包的表示方式类似,与域名的定义方向一一对应。 -
artifactId
*:定义该模块的名称。用于区分不同的功能模块。 -
version
*:定义该模块的版本定义 -
packaging
:定义该模块的打包方式(jar、pom、war),packaging是可选的,默认为jar
。 -
classifier
:该元素用来定义构建输出的一些附属构建。附属构建与主构件对应。(例如主构件的java文档和源代码 nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-sources.jar),不能直接定义!
依赖标签解析
根元素project下的dependencies可以包含一个或者多个dependency元素。
每个dependency元素包含的元素有:
-
groupId
*:(参考项目坐标) -
artifactId
*:(参考项目坐标) -
version
*:(参考项目坐标) -
type
:依赖的类型,对应于项目坐标定义的packaging,一般来说都不必声明默认是jar, -
scope
:依赖的范围 -
optional
:标记依赖是否可选 -
exclusions
:用来排除传递性依赖。
依赖范围
包含以下5中,system一般在项目中很少用到
- compile:编译依赖范围(默认),在编译、测试和运行中都需要使用该依赖。代表主键:spring-core
- test:测试依赖范围,只在测试有效,编译和运行时都无法使用该类依赖。代表主键:junit
- provided:已提供依赖范围,对在编译和测试有效,在运行时无效。代表主键:servlet-api
- runtime:运行时依赖范围。对测试和运行有效,编译无效。代表主键:mysql驱动包
- system:系统依赖范围,和provided依赖范围完全一致,此类依赖不是通过Maven仓库解析,依赖于操作系统的路径,所以移植性很差。必须通过systemPath元素来制定依赖文件的路径,该元素也可以引用环境变量。
依赖范围(Scope) | 对于编译classpath有效 | 对于测试classpath有效 | 对于运行时classpath有效 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | - | Y | - | junit |
provided | Y | Y | - | servlet-api |
runtime | - | Y | Y | jdbc驱动实现 |
system | Y | Y | - | 本地类库 |
依赖性和传递范围
A依赖于B,B依赖于C,则A对于B是第一直接依赖,B对于C是第二直接依赖。
第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围
左边是第一直接依赖,上边为第二直接依赖,中间的交叉单元格表示依赖传递的依赖范围。
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
依赖优先级原则
- 路径最近者优先:通过判断依赖路径的长度来优先使用路径较短的依赖
- 第一声明者优先:当依赖路径相等时,在pom文件中声明的顺序决定了那个依赖优先被解析使用。
各种依赖
可选依赖
A->B、B->X、B->Y
根据传递性依赖的定义,如果这三个依赖的范围都是compile,那么X、Y就是A的compile范围传递性依赖的定义。
然而,由于X、Y都是可选依赖,依赖将不会得以传递,即:X、Y将不会对A有任何影响
引入可选依赖特性可以解决X和Y的特性是互斥的情况,例如B是一个持久化层隔离工具包,依赖的X和Y是多种数据库驱动包,我们在项目中使用该工具包的时候只会依赖于一种数据库。
optional元素标记依赖为可选依赖
如果B的两个可选依赖为如下:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connection-java</artifactId>
<version>5.1.10</version>
<option>true</option>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.4-701.jdbc3</version>
<option>true</option>
</dependency>
</dependencies>
这两个依赖只会对B产生影响,当其他项目依赖于B的时候,这两个依赖不会被传递。
当A依赖于B的时候,可以显示的声明某个依赖
<dependencies>
<dependency>
<groupId>cn.lookzp.mvnbook</groupId>
<artifactId>B</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connection-java</artifactId>
<version>5.1.10</version>
</dependency>
</dependencies>
排除依赖
在dependency元素中使用exclusions
元素,exclusions中定义exclusion元素定义排除的依赖
<exclusions>
<exclusion>
<groupId>..</groupId>
<artifactId>...</artifactId>
</exclusion>
</exclusions>
归类依赖
如果多个依赖中使用的版本是一样的,或者方便管理依赖的版本,这时候可以使用归类依赖来声明依赖的版本信息。
在properties
中声明版本变量属性,在properties中声明的属性名称可以自定义。
在依赖中使用Maven的属性,就需要使用 ${元素名称} 的形式使用
<properties>
<springframework.version>2.5.6</springframework.version>
</properties>
优化依赖
可以通过mvn dependency:list
或者mvn dependency:tree
命令查看到当前项目解析的依赖。
mvn dependency:list
:罗列依赖
mvn dependency:tree
:以树的形式罗列
Maven在Eclipse中的使用
Maven目录结构
|- src
|-main
|-java 主目录代码
|-resources 配置文件(property、xml)
|-webapp web文件夹(和webcontext是一样的)
|-test
|-java 测试的java代码
|-resources 测试的配置文件(不配置也行)
在eclipse中配置Maven
m2e软件,一般在eclipse中已经集成,不需要自己安装。如果eclipse没有m2e插件,可以找到菜单项中的help
下的Install New Software...
,弹出框下的work with填写http://download.eclipse.org/technology/m2e/releases
下载路径,然后然后下载插件即可
eclipse里面自带里面的Maven版本可以用,但是一般配置自己Maven版本到项目中去
找到Installations和User Settings目录,
Installations:选择自己安装的Maven软件
User Settings:指向自己安装的Maven项目的settings.xml配置文件,本地仓库会自动定位到配置文件中的仓库地址
Maven项目构建
步骤:
1、 新建maven项目
2、 跳过骨架,不跳过骨架创建的项目是目录不全的
3、 填写骨架
- group id:域名倒写(如:cn.lookzp.hello)
- artifact id:开发模块的项目名称(如:HelloServelt)
- packaging:pom表示多人开发于一个项目,war表示的是web项目,jar表示打包后可以给其他项目引用
- name:项目名称(写不写意义不大)
- description:项目描述
4、 处理创建工程后的红色叉号
一个web工程是需要web.xml配置文件的,但是跳过骨架创建的Maven工程师没有web.xml文件的。
两种解决方式:
- 拷贝其他项目中的web.xml文件
- 项目生产web.xml文件
-
配置动态web模块的版本(默认是2.5版本),项目右键找到属性
image.png - 修改完后,需要更新一下maven,项目右键找到Maven -> Update Project
- 生成web.xml文件
image.png
很奇怪的是,每次生成web.xml都是修改Dynamic Web Module版本前的版本。所以我都是生成后删除再删除一边。如果有已有的项目中存在,可以直接导入该web.xml配置文件
-
5、 修改编译的版本(参考下面的在pom.xml配置文件中常用的插件
小节),可以不配置
修改后必须 Maven -> Update Project
注:Dynamic Web Module的版本依赖于jdk的版本
6、 添加tomcat版本(参考下面的在pom.xml配置文件中常用的插件
小节),可以不配置
修改后必须 Maven -> Update Project
7、 运行项目:
项目右键 -> Run At -> Maven build...
-> Goals
如果已经配置了的,以后运行直接找到Maven build
,点击运行即可
在pom.xml配置文件中常用的插件
处理编译版本
默认的编译版本为jdk1.5
<build>
<!-- 配置了很多插件 -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
了解:可以在settings.xml中配置全局的jdk编译版本
<profile>
<id>jdk18</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>
添加tomcat插件
默认的tomcat为6,在控制面板中启动tomcat6的执行命令为mvn tomcat:run
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8000</port>
<path>/hello</path>
</configuration>
</plugin>
在控制面板中启动tomcat7的执行命令:mvn tomcat7:run
Maven索引中找到依赖
Window -> show View -> Other -> Maven -> Maven Repositories
在视图中,右键Local Repository中添加索引
Maven中使用Debug模式
Debug模式的使用和tomcat的直接运行会有点不太一样!
Debug模式需要选择项目源来测试,在Source中,Add添加Project就能把项目加入到Debug调试模式中
Maven中快速添加junit测试
需要测试的类,右键
选择test目录
分模块开发Maven项目
在一个项目中,有可能存在多人开发的情况,这种情况就需要分工合作去开发,有可能一个Maven项目会分成几个模块进行开发,这样每个模块直接就需要衔接。这时候就需要依靠于私服来帮我们完成,将自己开发的模块发布到私服中,给其他开发成员下载自己的模块进行开发。
还记得前面讲到的pom打包模式吗?这个就是运用于多人开发模式的
-
创建Maven父工程,选择的packaging为:
pom
(多人开发)
image.png
父工程中的pom.xml文件,所有的依赖和插件都在父工程的pom.xml中配的好处是,子工程都可以通过继承的方式获得依赖,就不需要在子工程的pom.xml文件中进行配置
父工程的pom.xml文件配置 -
创建Maven子工程dao模块,选择的packaging为:
jar
,(这样就可以依赖给其他项目)
dao子工程模块
dao模块中的pom.xml配置
dao模块中的pom.xml配置
如果其他人需要依赖于你dao模块中的信息,你需要把自己的模块打包到私服中,在项目中右键执行命令install打包!然后再别人的项目中就可以通过依赖的方式把你的模块打包到本地中进行开发。
以此类推
注:对于web模块需要更改打包的方式为war