Maven 是一个强大的 Java 项目构建工具。 对于其他语言也是可以的,但是主要是 Java,是由 Apache 开发的。
主要作用就是:项目的构建和依赖管理
项目构建体现在:在项目编码完成后,要对项目进行编译、测试、打包、部署;这些工作 Maven 都可以做到
依赖管理它的好处体现于:
不需要再导入 jar 包,大大降低了项目文件的占用大小(它会根据配置的“坐标”去 Maven 仓库里去寻找对应的 jar 包)
安装
Maven 是用 Java 开发的,所以它的运行需要 JDK 支持,和 Java(JAVA_HOME) 类似,只需要把解压后的目录放进环境变量就行了,名称可以叫 MAVEN_HOME,然后再配进 path 里
然后在 CMD 命令行中输入 mvn -v
测试下是否正常
配置本地仓库
Maven 仓库存的是各种各样的 jar 包,可以在本地搭建也可以在局域网中搭建,或者直接用互联网上的中央仓库
配置本地仓库去修改 Maven 目录下的 conf 目录下的 settings.xml
文件,将 localRepository 标签配好即可
1 | <settings> |
如果是 Eclipse 然后可以找到 Maven 的视图生成下索引,这样有利于 jar 包的快速获取
新建一个Maven项目
首先选择 Create a simple project (skip archetype selection)
简单来说它的作用是给你创建好相应的文件目录,也就是骨架,如果不选择,你要手动选择一个模型,然后说下新建项目中需要填写的几个:
- Group Id
针对一个项目的普遍唯一识别符。是项目组织唯一的标识符,实际对应 JAVA 的包的结构,相当于我们日常使用的包名,例如:com.bfchengnuo - Artifact Id
要新建的项目的名字,就是项目的唯一的标识符,实际对应项目的名称,就是项目根目录的名称。 - Version
版本号,默认0.0.1-SNAPSHOT
,也就是测试版本 - Packing
要将该项目生成什么类型,有 jar、war、ejb、rar、pom(父工程)、eclipse-plugin 等等 - Name
名字(估计是模板的名字) - Description
说明
Maven项目目录结构
和一般的项目也没啥区别,都有 src 源码文件夹,只是多了一个 pom.xml
文件,这个是 Maven 项目的核心配置文件,里面可以配置 JDK 的版本、jar 包的相关信息。
另外,Maven 下的 src 文件夹是有一定结构的,里面分为两个文件夹,main 和 test,从名字可以看出一个是主要,一个是测试;main 里面继续分,分为 java、resources(项目所需的配置文件) 和 webapp 文件夹;没有 lib 了,因为没有 jar 包什么事了
|-ProjectName
|– pom.xml
|– src
|–|– main
|–|–|– java
|–|–|– resources
|–|–|– [ webapp/WEB-INF/web.xml ]
|
|–|– test
|–|–|– java
|–|–|– resources
最终 java 、resource 目录下的文件默认还是会发布在一起,和原来的结构类似,所以 classpath 后可以直接引用 resource 下的文件(貌似是可以手动设置 classpath 的位置的,和 Gradle 应该类似,待研究)
Maven常用命令
mvn clean
清理编译后的文件,也就是 targen 目录mvn compile
编译;编译后的文件在 targen 目录下mvn test
进行测试;运行测试目录下类的所有测试方法
注意类名的规范:xxxTest.java
mvn package
打包;默认会先编译再进行测试,最后执行打包,如果是 web 项目会自动打成一个 war 包,放在 targen 目录下mvn install
安装;把 jar 包安装到本地仓库中去,便于其他项目使用
过程还是先编译再测试,然后打包,最后再安装。也可以说是打包到本地仓库mvn tomcat:run
部署到 Tomcat (通过 war 包),并且启动
Maven项目生命周期
在 Maven 中存在三套生命周期,它们相互独立,互不影响(不同周期里的命令不会互相调用),在一套生命周期中,执行后面的命令,前面的命令会自动执行
- CleanLifeCycle:清理生命周期
包含命令:clean - DefaultLifeCycle:默认生命周期
包含命令:compile –> test –> package –>install –>deploy(上传到私服) - siteLifeCycle:站点生命周期
包含命令:site
POM常见配置
比如我们需要设置 JDK 的版本:
1 | <build> |
其实就是添加了一个插件,通常所有的项目都要定义一个共同的父工程,这个父工程没有代码仅仅是为了统一版本号和 jar 等问题,所以最终要的是 pom.xml 文件,还有一个空壳的 src 文件夹,那么就来看看常用的一些配置:
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
最重要的是 dependencyManagement 标签,它起到了统一管理依赖版本号的作用,使用这个标签后在子项目也必须配置 dependency 标签后相关的 jar 才会被导入到项目中,但是不用指定其版本,如果不加 dependencyManagement 在父工程定义的依赖都会自动导入到子工程中…..
子工程继承父工程非常简单,只需要加入:
1 | <parent> |
这样就相当于是继承了~解决了指定版本号的问题;另外 Maven 会自动处理 jar 的依赖,也就是说如果此 jar 依赖其他 jar ,会自动把所需的 jar 导入,不用再配。
依赖范围
依赖范围可分为:编译依赖范围、测试依赖范围、已提供依赖范围、运行依赖范围、系统依赖范围、导入依赖范围;
默认的依赖范围是 compile,手动指定就是在 dependency 中加一个 scope 标签,例如: <scope>test</scope>
依赖范围 | 对于编译有效 | 对于测试有效 | 对于运行有效 | 栗子 |
---|---|---|---|---|
compile | √ | √ | √ | spring-core |
test | - | √ | - | junit |
provided | √ | √ | - | servlet-api |
runtime | - | √ | √ | JDBC 驱动 |
system | √ | √ | - | Maven仓库之外的 |
import | - | - | - | - |
系统依赖范围通过 systemPath 显式指定;再说导入依赖范围,它不会对 classpath 产生影响。
servlet 相关的 API 一定要设置为 provided,否则会和 web 服务器中的 jar 冲突。
依赖范围除了控制 classpath,还会对依赖传递产生影响。如果 A 依赖 B,B 依赖 C,则 A 对于 B 是第一直接依赖。B 对于 C 是第二直接依赖。 A 对于 C 是传递性依赖。结论是:第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。
第一依赖\第二依赖 | compile | test | provided | runtime |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
第一列是第一直接依赖,第一行是第二直接依赖,中间表示传递性依赖范围。
可以理解为:第一行为 B 和 C 的范围;第一列为 A 和 B 的范围;中间围起来的区域就是 A 和 C 的范围
传递依赖冲突
根据上面的传递依赖,比如项目同时依赖 A 和 B,A 又依赖于 C 并且版本是 1.0;B 也依赖于 C ,但是版本是 2.0;如果都导入肯定会发生冲突
Maven 会自动处理一些冲突,其遵循两个原则:
- 第一声明者优先
就是谁先定义的就用谁的 - 路径近者优先
例如,项目中引入了 B 依赖,B 又间接引入了 C 依赖;如果后面单独定义了一个 C 的依赖,那么就会按照这个独立的依赖(第一依赖)的版本来
或者可以手动排除掉其中一个,这样就相当于只有一个,具体在 pom.xml 文件中配置,使用 exclusion 标签,比如:
1 | <dependency> |
还可以进行版本锁定这样就能确保只使用某一个版本:使用的是 dependencyMangedent 标签;它只起一个锁定的作用,不会进行导入包操作【推荐使用】
1 | <properties> |
一般情况下 dependencyManagement 都会定义在父工程下的,子工程引用时就不需要指定版本了。
注意:在 pom.xml 文件中可以定义属性,并且可以使用 OGNL 表达式,详细的栗子不表
概念模型
用一幅图片来表示就是:
上面的部分是处理依赖关系,下面的就是项目构建了…
可以看出项目构建依赖的是我们配置的插件
项目拆分
Maven 两个重要的特性就是继承和聚合(它们的类型都是 pom 工程),对于传统的 SSH 架构,可以对每一层进行拆分,然后还需要一个父工程来进行统一,例如可以把 Dao 层和相关配置文件拆成一个工程,这样可以进一步降低耦合度。
要知道 Spring 配置文件可以拆的,所以就好办了,可以把 dao 层相关的配置放到 spring-dao.xml 中,最后在 web.xml 配置中使用通配符配下就行了(classpath*:spring-*.xml
)。
如果拆成多个子工程,子工程之间有依赖,那么记得先安装到本地仓库(install)然后加入其依赖。 因为依赖的传递性,所以 web 层引用 service 层的 jar 后,就不需要再单独加 dao 层的依赖了。
并且所有的工程一般都会有一个顶级父工程,它并不含有代码,最大的作用就是进行版本的统一了吧,按照上面的拆分,它就有了三个孩子,对应那三层(Dao 和 Service 打成 jar 包)。
关于父工程
创建父工程和创建普通的 Maven 项目一样,就是选择打包的时候选择 pom
父工程不进行编码,只有一个 pom.xml 文件
既然不进行编码,那么它的作用是什么呢?
- 定义所需要的依赖信息,子模块可以直接继承使用
- 将各个子模块聚合到一起
一般都是把父工程安装到版本库中去的,就是前面的 install 命令,因为最终项目运行的时候第一步会去本地仓库找依赖,如果父工程不发布子工程也没法玩(找不到“依赖”),POM 文件中一般都会用上面说的解决冲突中的版本锁定方式来锁定版本。
注意:创建子工程的时候就要选择 Module 了,新建模块!
对于 service 和 dao 层,打成 jar 包就可以了,为了避免报错在 service 中引用 dao 层的依赖就行了,在 pom.xml 文件里配置,和一般的加依赖一样。执行的 install 命令其实就是打包到本地仓库啦~
对于 web 层,打包选择打 war 包;
部署测试的时候,记得先把父工程给安装(install )到本地仓库,要不就提示 “404” 错误 ;第一次运行会下载一些东西,后面就不会了
根据上面所说,父工程只有一个,但是很多情况下并不能满足需求,例如目前最常用的 SB、SC 系列需要使用父工程做版本仲裁,但是我们自己的工程也需要一个父工程,那么就可以这样解决,在我们自己的父工程中:
1 | <dependencyManagement> |
简单说就是通过 dependencyManagement 来导入其他的父工程。
运行
运行调试的时候可以直接运行父工程,也可以运行 web 层的子工程,还可以传统方式部署到 Tomcat 运行
Maven 项目的运行在右键菜单有两个选项,build 和 build… ,它们的区别是第一个是执行历史运行记录;第二个是手动输入指令运行,一般在 Goals 输入 tomcat:run
即可。
或者在 RunConfigurations 设置里直接 new 一个 Maven build 进行配置,比如最新的 tomcat7:tomcat7:run
当进行 debug 调试的时候,需要在 source 选项卡下关联本项目才行,否则断点进不了(Eclipse),所以一般还是部署到 Tomcat 调试比较省事,虽然 Maven 提供 build 方式
properties(Maven属性)
通过 <properties>
元素用户可以自定义一个或多个 Maven 属性,然后在 POM 的其他地方使用 ${属性名}
的方式引用该属性,这种做法的最大意义在于消除重复和统一管理。
Maven 总共有 6 类属性:内置属性、POM属性、自定义属性、Settings属性、java系统属性和环境变量属性
内置属性
两个常用内置属性${basedir}
表示项目跟目录,即包含 pom.xml 文件的目录;${version}
表示项目版本POM属性
用户可以使用该类属性引用 POM 文件中对应元素的值。如${project.artifactId}
就对应了<project>
中<artifactId>
元素的值;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17${project.build.sourceDirectory}:项目的主源码目录,默认为 src/main/java/
${project.build.testSourceDirectory}:项目的测试源码目录,默认为src/test/java/
${project.build.directory} : 项目构建输出目录,默认为target/
${project.outputDirectory} : 项目主代码编译输出目录,默认为target/classes/
${project.testOutputDirectory}:项目测试主代码输出目录,默认为target/testclasses/
${project.groupId}:项目的groupId
${project.artifactId}:项目的artifactId
${project.version}:项目的version,与${version} 等价
${project.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}自定义属性
上面版本锁定的时候用过了,就是 properties 标签里定义的那些;子模块继承后也是可以继续使用的。Settings属性
与 POM 属性同理,用户使用以settings.
开头的属性引用 settings.xml 文件中的 XML 元素的值Java系统属性
所有 java 系统属性都可以用 Maven 属性引用,如${user.home}
指向了用户目录环境变量属性
所有环境变量属性都可以使用以env.
开头的 Maven 属性引用,如${env.JAVA_HOME}
指代了 JAVA_HOME 环境变量的的值
配置默认JDK版本
在 Maven 的配置文件中加入:
1 | <profile> |
这样在创建项目的时候就是指定的版本了,默认好像是 1.5 来
私服搭建
关于局域网(私服)的搭建工具可以使用 nexus ;谷歌搜一下就有了。nexus install/start
可以安装、启动服务(nexus.properties 中可更改端口)
启动后可以通过浏览器来访问管理界面,默认账户、密码为:admin / admin123;
仓库类型分为四种,一种已经废弃了,就不说了
- hosted 宿主仓库
一般存放本公司开发的 jar 包,包括正式版、测试版、第三方版等 - proxy 代理仓库
中央仓库(主仓库)以及 Apache 下的测试版本 jar 包 - group 组仓库
组嘛,可以包含其他的仓库,比如上面的两个
上传命令:mvn deploy
;
当然在上传的前面需要先配置下 pom.xml 文件确定上传路径,还有在 Maven 客户端的配置文件中 写入认证信息:
1 | <!--pom 文件!将ssm_dao上传私服 --> |
会根据项目设置的版本上传到对应的仓库;如果版本为release则上传到私服的release仓库,如果版本为snapshot则上传到私服的snapshot仓库。
私服设置好后需要在本地 Maven 客户端中(settings.xml)配置,让其能连接到私服:
1 | <mirrors> |
然后这样 jar 包就会优先从私服寻找,找不到的时候再去互联网仓库;<server>
节点配置服务的账户密码,用于发布构件时进行身份和权限的认证。
下载构件这里使用的是 mirrors ,还有其他方案,但是推荐这种,算是简单的吧,mirror 相当于一个拦截器,它会拦截 maven 对 remote repository (包括私服和中央仓库)的相关请求,把请求里的 remote repository 地址,重定向到 mirror 里配置的地址。
这样一来,Maven 在找不到的情况下也不会访问中央仓库了,那本是 nexus 该做的事情。最好也配置下 profiles:
1 | <profiles> |
然后来解释下上面出现的几个标签的意思:
Profiles
作用:根据环境参数来调整构建配置的列表。
settings.xml 中的 profile 元素是 pom.xml 中 profile 元素的裁剪版本。
它包含了 id、activation、repositories、pluginRepositories 和 properties 元素。
如果一个 settings.xml 中的 profile 被激活,它的值会覆盖任何其它定义在 pom.xml 中带有相同 id 的 profile。Repositories
作用:远程仓库列表,它是 maven 用来填充构建系统本地仓库所使用的一组远程仓库。pluginRepositories
作用:发现插件的远程仓库列表。
和 repository 类似,只是 repository 是管理 jar 包依赖的仓库,pluginRepositories 则是管理插件的仓库。
maven 插件是一种特殊类型的构件。由于这个原因,插件仓库独立于其它仓库。pluginRepositories 元素的结构和 repositories 元素的结构类似。每个 pluginRepository 元素指定一个 Maven 可以用来寻找新插件的远程地址。ActiveProfiles
作用:手动激活 profiles 的列表,按照 profile 被应用的顺序定义 activeProfile。
最后来补充个 repositories 的详细配置:
1 | <repositories> |
关于 Maven 的其他配置就自行搜索吧
使用阿里云镜像
当你没有私服,中央仓库由于一些原因特别的慢,不能浪费青春,可以换用阿里的镜像,能大幅提高下载速度,只需要加入:
1 | <mirrors> |
这样就相当于“替换”了中央仓库,速度是不是明显加快了?
评论框加载失败,无法访问 Disqus
你可能需要魔法上网~~