Maven
什么是Maven?
如何回答这个问题要看你怎么看这个问题。 绝大部分Maven用户都称Maven是一个"构建工具":一个用来把源代码构建成可发布的构件的工具。 构建工程师和项目经理会说 Maven是一个更复杂的东西:一个项目管理工具。那么区别是什么? 像Ant这样的构建 工具仅仅是关注预处理,编译,打包,测试和分发。像 Maven 这样的一个项目管理工 具提供了构建工具所提供功能的超集。 除了提供构建的功能,Maven还可以生成报告, 生成Web站点,并且帮助推动工作团 队成员间的交流。一个更正式的 Apache Maven 1 的定义: Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(ProjectLifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。 当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后 Maven 可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。别让Maven是一个"项目管理"工具的事实吓跑你。如果你只是在找一个构建工具,Maven能做这个工作。 事实上,本书的一些章节将会涉及使用Maven来构建和分发你的项目。
Maven 的好处
大家会发现,在平时项目中,我们每天有相当一部分时间花在了编译、运行单元测试、生成文档、打包和部署等烦琐且不起眼的工作上,如果你使用Ant,会发现很多东西都要程序员去定义,去编写build文件,然而有了Maven这个构建工具,能够帮我们自动化构建过程,从清理、编译、测试到生成报告,再到打包和部署。我们不需要也不应该一遍又一遍地输入命令,一次又一次地点击鼠标,或者小心翼翼的写着配置文件,我们要做的是使用Maven配置好项目,然后输入简单的命令(如mvn clean install),Maven会帮我们处理那些烦琐的任务。这一切都体现了一句经典“约定优于配置”,当然Maven不仅是构建工具,还是一个依赖管理工具和项目信息管理工具,况且Maven也是跨平台的。这些都会通过实例说明。
获取和安装
获取
下载地址:http://maven.apache.org/download.cgi ,进去之后,会发现有很多版本,我们将使用较新的版本apache-maven-3.1.1-bin.zip, (windows平台)如图1所示:
如果想查看源码可以下载src版本,当然也可以选择其他操作系统,比如linux平台下的文件,本系列只介绍windows平台下,其他平台还请参照其他博文讲解。 找到下载后的文件,并将文件解压到一个自己定义的目录,如图2所示:
此时发现有四个目录,下面讲解四个目录中文件的作用
bin目录:
该目录包含了mvn运行的脚本,这些脚本用来配置java命令,准备好classpath和相关的java系统属性,然后执行java命令
boot目录:
该目录只包含一个文件,该文件为plexus-classworlds-xxx.jar。plexus-classworlds是一个类加载器框架,相对于默认的java类加载器,它提供了更丰富的语法以方便配置,maven使用该框架加载自己的类库。
conf目录:
该目录包含了一个非常重要的文件settings.xml。直接修改该文件,就能在机器上全局地定制maven的行为。一般情况下,我们更偏向于复制该文件至~/.m2/目录下(~表示用户目录),然后修改该文件,在用户范围定制maven的行为。
lib目录:
该目录包含了所有maven运行时需要的java类库。
安装
环境变量的配置
打开系统属性(桌面上右键单击“我的电脑”→“属性”),点击高级系统设置,再点击环境变量,在系统变量中新建一个变量,变量名为M2_HOME,变 量值为Maven的安装目录E:/StudySoftware/javaEE/apache-maven-3.1.1(根据自己安装的情况)。点击确定, 接着在系统变量中找到一个名为Path的变量,在变量值的末尾加上%M2_HOME%/bin;,注意多个值之间需要有分号隔开,然后点击确定。至此,环境变量设置完成,详细情况如图3所示:
下面将查看配置是否正确,打开命令控制台,输入mvn -v如果发现图4所示的信息,配置就是成功的:
至此,Maven的环境变量配置成功。
设置本地仓库
由于运行过mvn命令,那么会在用户目录下发现一个.m2文件,如图5所示:
其中repository目录,这个目录是存放从网上下载下来的jar,而这些jar就是maven的好处之一,可以自动下载项目中用到的jar包,而这些jar包的存放位置就是一个仓库,存放在本地的就是本地仓库,而存放在网络上的就是maven的*仓库。 由于下载jar文件的增多,会给C盘造成负担,或者在重装系统后,c盘的东西将不复存在,因此可以把repository目录转移到其他盘中储存。
具体做法是:
A. 复制repository目录到其它盘符,本文中是在E:\Workspaces\Maven下
B. 复制刚刚解压好的Maven目录中,conf中的settings.xml文件到E:\Workspaces\Maven目录下,
C. 打开找到如下内容 <localRepository>....</localRepository>改成:
<localRepository>E:/Workspaces/Maven/repository</localRepository>
同样将conf下的settings.xml也改成如上配置,如图6所示:
保存.完成.
这样就将本地仓库从C:\Users\Steven\.m2\repository移到了E:\Workspaces\Maven\repository中。
创建简单的Maven实例
使用骨架创建maven项目
使用默认的骨架创建项目
使用 mvn archetype:generate创建maven骨架
如图,创建一个Maven项目
这样会生成两个目录:
C:\Users\Administrator\aaTest\src\main\java\com\aayongche\aaTet目录,这是一个正式代码目录
C:\Users\Administrator\aaTest\src\test\java\com\aayongche\aaTet,这是一个测试代码目录。
这是一个简单的Maven项目,使用其他骨架可以生成适合我们开发的目录结构。
骨架介绍
骨架:是Maven 内置的骨架插件,他可以给我们生成符合开发的目录结构,不用我们自己定义目录。
1: internal -> appfuse-basic-jsf (创建一个基于Hibernate,Spring和JSF的Web应用程序的原型)
2: internal -> appfuse-basic-spring (创建一个基于Hibernate,Spring和Spring MVC的Web应用程序的原型)
3: internal -> appfuse-basic-struts (创建一个基于Hibernate,Spring和Struts 2的Web应用程序的原型)
4: internal -> appfuse-basic-tapestry (创建一个基于Hibernate, Spring 和 Tapestry 4的Web应用程序的原型)
5: internal -> appfuse-core (创建一个基于 Hibernate and Spring 和 XFire的jar应用程序的原型)
6: internal -> appfuse-modular-jsf (创建一个基于 Hibernate,Spring和JSF的模块化应用原型)
7: internal -> appfuse-modular-spring (创建一个基于 Hibernate, Spring 和 Spring MVC 的模块化应用原型)
8: internal -> appfuse-modular-struts (创建一个基于 Hibernate, Spring 和 Struts 2 的模块化应用原型)
9: internal -> appfuse-modular-tapestry (创建一个基于 Hibernate, Spring 和 Tapestry 4 的模块化应用原型)
10: internal -> maven-archetype-j2ee-simple (一个简单的J2EE的Java应用程序)
11: internal -> maven-archetype-marmalade-mojo (一个Maven的 插件开发项目 using marmalade)
12: internal -> maven-archetype-mojo (一个Maven的Java插件开发项目)
13: internal -> maven-archetype-portlet (一个简单的portlet应用程序)
14: internal -> maven-archetype-profiles ()
15: internal -> maven-archetype-quickstart ()
16: internal -> maven-archetype-site-simple (简单的网站生成项目)
17: internal -> maven-archetype-site (更复杂的网站项目) 18: internal -> maven-archetype-webapp (一个简单的Java Web应用程序)
19: internal -> jini-service-archetype (Archetype for Jini service project creation)
20: internal -> softeu-archetype-seam (JSF+Facelets+Seam Archetype)
21: internal -> softeu-archetype-seam-simple (JSF+Facelets+Seam (无残留) 原型)
22: internal -> softeu-archetype-jsf (JSF+Facelets 原型)
23: internal -> jpa-maven-archetype (JPA 应用程序)
24: internal -> spring-osgi-bundle-archetype (Spring-OSGi 原型)
25: internal -> confluence-plugin-archetype (Atlassian 聚合插件原型)
26: internal -> jira-plugin-archetype (Atlassian JIRA 插件原型)
27: internal -> maven-archetype-har (Hibernate 存档)
28: internal -> maven-archetype-sar (JBoss 服务存档)
29: internal -> wicket-archetype-quickstart (一个简单的Apache Wicket的项目)
30: internal -> scala-archetype-simple (一个简单的scala的项目)
31: internal -> lift-archetype-blank (一个 blank/empty liftweb 项目)
32: internal -> lift-archetype-basic (基本(liftweb)项目)
33: internal -> cocoon-22-archetype-block-plain ([ http://cocoapacorg2/maven-plugins/ ])
34: internal -> cocoon-22-archetype-block ([ http://cocoapacorg2/maven-plugins/ ])
35: internal -> cocoon-22-archetype-webapp ([ http://cocoapacorg2/maven-plugins/ ])
36: internal -> myfaces-archetype-helloworld (使用MyFaces的一个简单的原型)
37: internal -> myfaces-archetype-helloworld-facelets (一个使用MyFaces和Facelets的简单原型)
38: internal -> myfaces-archetype-trinidad (一个使用MyFaces和Trinidad的简单原型)
39: internal -> myfaces-archetype-jsfcomponents (一种使用MyFaces创建定制JSF组件的简单的原型)
40: internal -> gmaven-archetype-basic (Groovy的基本原型)
41: internal -> gmaven-archetype-mojo (Groovy mojo 原型)
创建其他骨架语法
A、在命令窗口中输入mvn archetype:generate如下图:
B、回车执行之后出现会看到一段长长的输出,有很多可用的archetype供我们选择,每一个archetype前面都会对应有一个编号,同时命令行会提示一个默认的编号,如下图,其对应的archetype为maven-archetype-quickstart,我们直接回车以选择该archetype:
C、然后Maven会提示我们输入要创建项目的groupId、artifactId、version、以及包名package,按照下图所示填写。最后出现确认提示,输入Y。看到BUILD SUCCESS表示成功完成mvn project的构建。
自己编写Maven格式的项目
本文在目录E:/Workspaces/STSWorkspace/Maven_01(可以随便找个目录)下,新建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/maven-v4_0_0.xsd " >
<modelVersion>4.0.0</modelVersion>
<groupId>com.steven.maven</groupId>
<artifactId>maven_01</artifactId>
<version>0.0.1-SNAPSHOT</version>
</project>
其中
第一行是xml的头,定义了xml文档的版本和编码。
紧接着是project元素,该元素是所有元素的根节点,声明了一些POM相关的命令空间及xsd元素。
根元素的下一个元素是modelVersion 指定了当前POM 模型的版本,对于Maven2和Maven3来说,都是4.0
groupId:项目或者组织的唯一标志,并且配置时生成路径也是由此生成,如com.steven.maven生成的相对路径为:/com/steven/maven
artifactId:项目的通用名称
version:项目的版本 eg:x.x.x-里程碑号(SNAPSHOT->ALPHA->BETA->RELEASE)
接下来在此文件夹中建立src文件夹,然后在src中建立main和test文件夹,然后在main中建立java文件夹,然后在java文件夹中建立com/steven/maven/HelloWorld.java文件,如图7所示:
HelloWorld.java的内容如下:
package com.steven.maven;
public class HelloWorld{
public void sayHello(){
System.out.println("Hello Maven!");
}
}
然后在命令控制台中执行mvn compile,如图8所示:
会发现出现Downloading下载的提示,这是因为,Maven在执行命令的时候,要加载其他使用到的插件和其他jar包,称为依赖,这个时候就要从 网络上进行下载,所以如何使用Maven,请保持网络是正常的,否则会出现问题。当编译完成的时候出现成功的提示信息,如图9所示:
此时发现项目目录中多了一个target的目录,如图10所示:
发现刚刚编译产生的class文件放在了target目录下,所以只要我们按照这个目录格式编写,maven工具就帮助我们执行出结果,然后放在相应的结构目录下。 下面我们将继续编写测试代码,在test目录下新建java文件夹,然后同样新建com/steven/maven/HelloWorldTest.java文件,其中HelloWorldTest文件的内容如下:
package com.steven.maven;
import org.junit.*;
public class HelloWorldTest{
@Test
public void sayHelloTest(){
HelloWorld hw = new HelloWorld();
hw.sayHello();
}
}
此时的目录结构如图11所示:
然后控制台执行mvn test命令,但此时出现错误信息,如图12所示:
这是因为测试过程中用到了junit的包,但没有找到,原来的解决办法是:
通过将junit的包加入到环境变量下即可解决
但maven的解决思路是:在pom.xml中加入<dependencies>节点,完成的配置文件如下所示:
<?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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.steven.maven</groupId>
<artifactId>maven_01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
<dependencies>节点代表项目依赖的jar包,然后加入junit的jar包配置文件即可,此时再次执行mvn test,会发现如图13所示的结果:
这时候maven从网络上下载4.10版本的junit jar包,最后的执行结果如图14所示:
此时发现测试成功,正确执行Hello Maven!的结果。 此时再来看target中的文件会发现如图15所示的结果:
如果此时执行mvn clean命令,如图16所示:
会发现在下载相应插件的同时,执行了清除target目录的操作,然后再来看Maven_01目录中的文件,如图17所示:
发现target目录已经不存在了,即被maven clean命令清空了。如果接着执行maven test,会发现target目录又出现了。 这个时候再来观察一下一开始配置的本地仓库中的文件,会发现图18的结果:
我们发现,Maven在执行命令的过程中用到的jar都已经在本地仓库中储藏了,至此,Maven小实例结束。
总结:
在本节中,通过Maven的介绍,下载,安装,配置,以及小案例的实现,估计大家也多多少少有些头绪了,通过案例,可以看出Maven可以作为项目的构建工具,而配置文件很好写,而且写的很少,用到的jar包,不需要手动去添加到环境变量中,
只要通过配置,Maven就会通过网络寻找到并且下载到本地仓库中。
在本节中用到的Maven命令:
mvn compile编译java文件
mvn test执行junit操作
mvn clean清空target目录操作
使用IDEA 创建Maven项目
(1)点击settings ,如下图设计maven为本地自己的Maven,不要使用编译器自带的Maven插件。
(2)依次点击File 、new 、Maven,选择适合自己的骨架, 如下图:
(3)填写GroupId、ArtifactId、version,如下图:
(4)查看并关联自己的Maven配置,如下图:
(5)填写自己的项目名
(6)产生的目录结构
(7)主要使用IDEA右侧的Maven操作列表
依赖管理
项目构建
通过一个典型的项目构建过程来学习Maven的依赖传递性。 总共分为三个模块的项目编写user-core,user-log,user-service,在user-core中主要实现基本的实体类,初始文件,dao的编写,在user-log中简单模拟日志文件的记录,在user-service模块中主要引入前两个模块,从而进行编写service模块的功能,在这里模块的编写不是重点,这些都会给出代码,然而进行Maven依赖与传递的讲解才是重点,所以很多时候代码都是为了演示出效果,和实际开发还是有些差别,但所指代的思想是项目中所存在的。
依赖的定义和好处
有人认为Maven是一个依赖管理工具,当然这种想法是错误的(确切的说Maven是一个项目管理工具,贯穿了整个项目生命周期,编译,测试,打包,发布...),但Maven给人造成这种错误的印象也是有原因的,因为Maven的依赖管理十分强大,用好了Maven,你不再需要面对一大堆jar感到头大,依赖冲突,无用依赖等问题也能够得到有效的防止和解决。本节介绍如何用好Maven的依赖管理。 取自:http://juvenshun.iteye.com/blog/337405
依赖用法
Maven的依赖是使用Maven坐标来定位的,而Maven坐标主要由GAV(groupId, artifactId, version)构成,因此,使用任何一个依赖之间,你都需要知道它的Maven坐标,在之前的第二章节中,从引入了Maven的*工厂的网页,在此引入一个常用的Maven搜索jar包的资源库网页http://www.mvnrepository.com/,在此可以查找所需要的jar文件的GAV,比如在5.2节中有如下配置:
<span style="font-family:KaiTi_GB2312;font-size:18px;">
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</span>
可以通过如下列图所示的步骤进行查找到,而且以后的jar包查找过程都是按照此步骤进行获取,然后复制到pom.xml<dependencies>元素下,Maven就能在本地或者远程仓库中找到对应的jar包(不一定可以找到所有的jar文件)
此配置声明了一个对junit的依赖,它的groupId是junit, artifactId是junit, version是4.10。这一组GAV构成了一个Maven坐标,基于此,Maven就能在本地或者远程仓库中找到对应的junit-4.10.jar文件。
依赖传递
创建user-core
首先紧跟第二章节的新建项目构建出如下图所示的结构图,
通过此模块的构建,主要说明Maven的一下几个问题:
A、目录结构的默认构建,比如资源文件的src/main/resources目录的构建
B、pom.xml文件的使用到的jar包的引入,比如在user-core中的文件内容如下所示:
通过资源库中查找到相应的jar包,放到pom.xml中之后,IDEA就会自动会和本地资源库或者远程仓库中进行匹配,如果本地中没有,就会通过网络下载从远程仓库中下载到本地仓库中。
C、进行Maven项目模块的打包和加入到本地资源库的命令,如图所示:
这样就可以执行打包或者将打包后的jar添加到本地仓库中。
接着引入user-log,user-service
其中user-log的pom.xml如下所示:
其中user-service的pom.xml如下所示:
此时细心的读者会发现在user-service的pom文件中出现${project.groupId}和${project.version}的写法,这是Maven的内置隐式变量,常用的如下介绍:
${basedir} 项目根目录
${project.build.directory} 构建目录,缺省为target
${project.build.outputDirectory} 构建过程输出目录,缺省为target/classes
${project.build.finalName} 表示输出文件名称,缺省为${project.artifactId}
${project.version} 版本号
${project.packaging} 打包类型,缺省为jar
${project.xxx} 当前pom文件的任意节点的内容(这里所使用到的)
注意:
在完成user-service对user-log和user-service模块的jar包引入的时候,一定要将这两个模块进行install处理,这样在user-service中才可以访问到前两个模块的jar包。
在IDEA 中查看依赖关系
如图,点击视图按钮,查看依赖关系。
依赖的难点
(1)依赖级别不相同时:
A--->l1.0
B--->l2.0
c---->A,B
在C的POM中,谁先声明依赖谁
(2)依赖级别相同时:
依赖层级最短的那个。
排除依赖
Maven很智能,在此提供一个元素,exclusion,只要在不想要的模块中添加此元素,比如不想要user-log的dom4j1.5版本,则只要在user-service中这样编写即可:
依赖的范围
A. 好处
Maven在编译主代码的时候需要使用一套classpath,在编译和执行测试的时候会使用另一套classpath,实际运行项目的时候,又会使用一套classpath。
依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系,
B.Maven有以下几种依赖范围:
compile: 编译依赖范围。
如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。 test: 测试依赖范围。
使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子就是JUnit,它只有在编译测试代码及运行测试的时候才需要。这也是本文的pom文件中为何junit的scope是使用test范围的原因。 provided: 已提供依赖范围。
使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。 runtime: 运行时依赖范围。
使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
注意:以上四种范围是开发中常用的配置,而以下两种都是不要求的,使用的很少,了解即可
system: 系统依赖范围。
该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。 import(Maven 2.0.9及以上): 导入依赖范围。
聚合和继承
聚合
把同一个项目中的很多模块,使用一个外部的pom文件统一管理,简化每次都要分别编译不同模块的。
继承
多模块开发时,写一个公共的父类,把各个模块的配置写入这个POM文件中,这时子类只需要引入自己所需的配置即可。
父类写法:
子类的写法:
Maven 生命周期
就像人生一样,出生,成长,死亡,这个每个人乃至整个生命都拥有的特性。Maven的构造者就发现,整个项目的构建过程都拥有高度的相似性,清理、初 始化、编译、测试、打包、集成测试、验证、部署、站点发布等等,于是,Maven就在这些过程中抽象出三套执行机制,也就是独立的三种生命周期,分别是:
Clean Lifecycle 项目的构建之前进行清理操作
Compile(Default) Lifecycle 主要进行项目构建的操作,编译测试打包部署等核心操作
Site Lifecycle 项目报告的生成,站点发布操作
完整生命周期(供参考)
A.Clean
pre-clean 执行一些清理前需要完成的工作。 clean 清理上一次构建生成的文件 post-clean 执行一些清理后需要完成的工作
B.Compile(Default)
validate 验证项目是正确的并且所有必需的信息都是可用的
initialize 初始化构建状态
generate-sources 产生所有的用来编译的源代码
process-sources 处理源代码
generate-resources 产生包含在package中的资源
process-resources 复制和处理资源到目标目录,为打包做好准备
compile 编译项目的主源代码 process-classes 对编译器产生的文件进行后期处理
generate-test-sources 产生所有用来编译的测试源代码
process-test-sources 处理测试源代码
generate-test-resources 创建测试所需要的资源
process-test-resources 复制和处理资源到测试目标目录
compile 编译测试源代码到目标目录
process-test-classes 对编译测试源代码产生的文件进行后期处理
test 使用适当的单元测试框架运行测试,这些测试代码不应该被打包或者部署
prepare-package 执行所有需要执行的操作为打包做准备,这往往会产生一个还未打包的处理过的包版本
package 使用设置的方式对编译过的文件进行打包
pre-integration-test 执行一些集成测试执行前必须的操作
integration-test 处理和部署包到集成测试环境中,进行测试
post-integration-test 对集成测试进行后期处理
verify 执行所有检查确保包是正确的和符合质量要求的
install 将包安装至本地仓库,以让其它项目依赖
deploy 将最终的包复制到远程的仓库,以让其它开发人员与项目共享
C.Site
pre-site 前期准备
site 产生项目的站点文档
post-site 后期处理
site-deploy 部署站点到服务器
插件机制
虽然Maven生命周期中有这么多个执行过程,但这些都是一个抽象的概念,这个概念性的东西意味着它并不做任何实质性的事情,也就是说:它就像接口,只定义规范,具体细节它不管。具体的 实现细节则交给了Maven 的各个丰富的插件,也就是说Maven的插件是要完全依赖于Maven的生命周期的,根据周期中的这些执行过程,才可以定义出相应功能的插件。
因此Maven的生命周期和Maven插件是相辅相成的,只有周期,没有插件,Maven是没有法子执行相应的操作,只有插件,没有周期,插件将失去了意义,在这里,可以很好的赞叹Maven的开发者多么智慧的头脑,以及多好的设计原则。
Nexus
Nexus 简介
前边简单介绍了Maven,而Maven默认提供的*仓库是在远程网络服务Appache提供的,这对于我们开发时不合理的。如果我们没网了或者什么情况,我们怎么办?也就是说我们队*仓库的依赖性太大。而Nexus私服则可以解决我们这个问题。先看下这张图应该大家就非才明白了:
这样就相当于在我们本地的局域网搭建了一个类似*仓库的服务器,我们开始将*仓库的一些资料下载到私服务器上,然后平时我们的maven项目就是直接访问局域网内的私服即可,既节省了网络带宽也会加速项目搭建的进程,这样对我们开发来说,对公司来说都是非常好的选择。
Nexus 下载安装
下载
下载地址:http://www.sonatype.org/nexus/go
Nexus 启动
我下载的是zip包,解压后进入\nexus-2.1.2-bundle\nexus-2.1.2\bin\jsw\,根据操作系统类型选择文件夹,我选的是windows-x86-32文件夹,进入后可看到如下所示bat文件。
双击console-nexus.bat运行。游览器中输入http://127.0.0.1:8081/nexus/,出现如下图所示就代表nexus已经启动成功。
8081为默认的端口号,要修改端口号可进入nexus-2.1.2-bundle\nexus-2.1.2\conf\打开nexus.properties文件,修改application-port属性值就可以了。
默认的用户名和密码:admin/admin123,登录后看到如下图所示:
Nexus 仓库介绍
点击左边导航栏的Repositories选项,会显示一个所有仓库及仓库组的列表,Type字段的值有group,hosted,proxy,virtual(在Maven1中使用,这里不需要关心),如图14所示:
在这里:
hosted,本地仓库,这个仓库主要是用来存放本地的依赖包,服务于局域网,不访问公网
proxy,代理仓库,用来存放nexus从公网中获取的依赖包,这里有*仓库,Apache和Codehaus开源网站的依赖包
group,仓库组,用来合并多个hosted/proxy仓库,通常我们配置maven依赖仓库组
比如点击Type为group,如图15所示:
会发现,在Ordered Group Repositories中,有所有hosted和proxy的仓库,而图15中箭头所指的地方有四种类型的仓库,分别是Releases(hosted), Snapshots(hosted), 3rd Party(hosted),Central(proxy),而Central指的是*仓库,存在于apache-maven-3.1.1\lib\maven-model-builder-3.1.1.jar中的org\apache\maven\model\pom-4.0.0.xml文件中,定义了*仓库的位置,配置文件如下所示。
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>http://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
其中url指向了*仓库的位置http://repo.maven.apache.org/maven2,而Releases, Snapshots, 3rd Party,主要功能如下:
Releases: 这里存放我们自己项目中发布的构建, 通常是Release版本
Snapshots: 这个仓库非常的有用, 它的目的是让我们可以发布那些非release版本, 非稳定版本, 比如我们在trunk下开发一个项目,在正式release之前你可能需要临时发布一个版本给你的同伴使用, 因为你的同伴正在依赖你的模块开发,
那么这个时候我们就可以发布Snapshot版本到这个仓库, 你的同伴就可以通过简单的命令来获取和使用这个临时版本
3rd Party: 顾名思义, 第三方库, 你可能会问不是有*仓库来管理第三方库嘛, 没错, 这里的是指可以让你添加自己的第三方库, 比如有些构件在*仓库是不存在的. 比如你在*仓库找不到Oracle 的JDBC驱动, 这个时候我们就需要自己添加到3rd party仓库
Nexus 仓库配置
既然这个group已经包含了四个仓库,那么只要将这个组进行配置,在Maven中就会引用所有在这个组对应的仓库中的依赖包,配置的URL地址为http://localhost:8081/nexus/content/groups/public/。我们可以配置在项目中的user-parent的pom.xml文件中,但此时会有个问题,这样的配置仅仅是对当前的项目有效(user-parent是父文件,其子文件继承),如果项目有很多,而不需要每个项目都要进行以上设置,也就是只要设置一次,然后本机的项目就会自动从nexus中寻找依赖包,如何做呢? 因为Maven在本机中是同一个,所以只要在Maven所对应的settings.xml中进行配置就好,(本文章settings.xml对应的路径为E:\Workspaces\Maven\settings.xml),配置如下:
<profiles>
<profile>
<id>nexusProFile</id>
<repositories>
<repository>
<id>localNexus</id>
<name>Nexus Repository</name>
<url>http://localhost:8081/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<!-- 此选项默认是关闭的,手动打开 -->
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<!-- 激活上面的配置 -->
<activeProfile>nexusProFile</activeProfile>
</activeProfiles>
Maven中的profile是一组可选的配置,可以用来设置或者覆盖配置默认值。有了profile,你就可以为不同的环境定制构建。
这个时候,做个试验,可以在user-core的pom.xml中加入以下依赖(这个依赖包并不存在于本地或者Nexus仓库中)
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.1.1</version>
</dependency>
添加好后进行保存,会发现Eclipse的控制台中有如图16的信息:
这表明,Maven已经从通过Nexus下载依赖包了,而Nexus从*工厂中进行下载。 接着看图17:
这也验证了Nexus已经将mybatis的依赖包下载到了仓库中。
Nexus 配置镜像
A. 但此时会有个问题,如果将Nexus服务停止。这个时候在user-core的pom.xml中添加原来没有的依赖配置文件(可以随便找个不存在的jar依赖文件进行测试,这里用使用Spring)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.2.RELEASE</version>
</dependency>
点击保存,会发现如下的信息
1. 14-3-7 GMT+0800下午8:29:41: [INFO] Using org.eclipse.m2e.jdt.JarLifecycleMapping lifecycle mapping for MavenProject: com.steven.user:user-core:0.0.1-SNAPSHOT @ E:\Workspaces\STSWorkspace\user-core\pom.xml.
2. 14-3-7 GMT+0800下午8:29:42: [INFO] Number of application's worked threads is 4
3. 14-3-7 GMT+0800下午8:29:44: [INFO] Downloaded http://localhost:8081/nexus/content/groups/public/org/springframework/spring-core/3.2.2.RELEASE/spring-core-3.2.2.RELEASE.pom
4. 14-3-7 GMT+0800下午8:29:44: [INFO] Number of application's worked threads is 4
5. 14-3-7 GMT+0800下午8:29:47: [INFO] Downloading http://repo.maven.apache.org/maven2/org/springframework/spring-core/3.2.2.RELEASE/spring-core-3.2.2.RELEASE.pom
6. 14-3-7 GMT+0800下午8:29:47: [INFO] Downloaded http://repo.maven.apache.org/maven2/org/springframework/spring-core/3.2.2.RELEASE/spring-core-3.2.2.RELEASE.pom
首先通过Nexus下载,但服务已经关闭,这个时候仍然可以下载,而且通过*仓库进行下载。但在项目中,不允许本地仓库直接下载*仓库的依赖包,这个时候就需要进行对*仓库进行覆盖,使之只能通过Nexus访问*仓库,这个时候需要对镜像进行配置。
B. 在settings.xml中配置镜像 首先配置镜像,使得只有通过Nexus才可以访问*仓库
<mirror>
<id>nexusMirror</id>
<mirrorOf>*</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://localhost:8081/nexus/content/groups/public/</url>
</mirror>
这里的*号代表所有的仓库都是通过这个url地址访问,这个时候可以附加一段配置,原来的*仓库中snapshots版本的依赖包默认是不可以下载的,但可以通过以下配置进行修改
</profiles>
<profile>
<id>centralProFile</id>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<!--由于配置过镜像,这个url不起作用-->
<url>http://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<!--覆盖*仓库中的false配置,可以从*仓库中下载snapshot版本-->
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<!-- 激活上面的配置 -->
<activeProfile>centralProFile</activeProfile>
</activeProfiles>
这样进行保存后,既可生效。 注意:配置文件settings.xml中会默认有相应的mirror和profile的例子,但都是注释掉的,我们在进行以上的改动时候,可以进行对其复制粘贴后进行修改,这样不容易出错。
C.验证:(此时没有打开nexus服务) 随便找个不存在的jar依赖文件进行测试,
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.26</version>
</dependency>
跟刚才的步骤一样,但此时保存后,Eclipse控制台中的信息如下所示:
1. 14-3-7 GMT+0800下午9:13:58: [INFO] Downloaded http://localhost:8081/nexus/content/groups/public/org/mortbay/jetty/jetty/6.1.25/jetty-6.1.25.pom
2. 14-3-7 GMT+0800下午9:13:58: [INFO] Using org.eclipse.m2e.jdt.JarLifecycleMapping lifecycle mapping for MavenProject: com.steven.user:user-service:0.0.1-SNAPSHOT @ E:\Workspaces\STSWorkspace\user-service\pom.xml.
3. 14-3-7 GMT+0800下午9:13:58: [INFO] Number of application's worked threads is 4
4. 14-3-7 GMT+0800下午9:14:02: [INFO] Using 'UTF-8' encoding to copy filtered resources.
5. 14-3-7 GMT+0800下午9:14:02: [INFO] skip non existing resourceDirectory E:\Workspaces\STSWorkspace\user-service\src\main\resources
6. 14-3-7 GMT+0800下午9:14:02: [INFO] Using 'UTF-8' encoding to copy filtered resources.
7. 14-3-7 GMT+0800下午9:14:02: [INFO] skip non existing resourceDirectory E:\Workspaces\STSWorkspace\user-service\src\test\resources
这里只能通过Nexus下载依赖包,但是由于服务停止,所以下载失败。
通过Maven 部署项目到Nexus
当项目已经编写完成,需要部署到Nexus中,这样团队人员可以通过Nexus下载到自己的本地仓库中,比如说,我是编写user-core的模块的,部署到Nexus中,需要以下两个步骤:
1、需要配置user-core的pom.xml文件,定义发布的版本以及发布到Nexus本地库的哪个仓库中,具体如下所示:
<distributionManagement>
<repository>
<id>user-core-release</id>
<name>user core release</name>
<url>http://localhost:8081/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>user-core-snapshot</id>
<name>user core snapshot</name>
<url>http://localhost:8081/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
2、配置本地settings.xml文件,让部署过程中有足够的权限,而Nexus中本来存在的有三个用户,如图21所示:
而部署使用deployment用户,在settings.xml具体的配置如下:
<server>
<id>user-core-release</id>
<username>deployment</username>
<password>deployment123</password>
</server>
<server>
<id>user-core-snapshot</id>
<username>deployment</username>
<password>deployment123</password>
</server>
完成以上两个步骤,此时进行部署(deploy)操作,如图22所示:
这个时候部署成功,然后我们可以在Nexus中看到部署后的结果,如图23所示:
在Snapshots的索引中可以查看到刚刚部署的user-core模块。
通过Nexus搜索构件
首先我们来明确一下构件的含义,
构件:构件是系统中实际存在的可更换部分,它实现特定的功能,符合一套接口标准并实现一组接口,而在我们这里就是我们要使用的即将需找的依赖。
在我们实际使用构件的过程中通常遇到一个问题,有时候我紧紧知道我所需要的构建的大致名字,并不知道全称或group id, 这是件非常头疼的事情. Nexus基于Nexus indexer(索引)的搜索功能帮我们解决了这个问题。
还记得刚才我们在解决Could not calculate build plan...问题的时候,给出的思路1,其思想就是,通过更改Download Remote Indexes的状态为True,使得Nexus从*仓库中将所有构件的信息同步到本地,这样在以后的开发中,就可以直接下载私有仓库中的依赖,就不需要Nexus还要去*仓库中下载了。下面就来领略Nexus搜索构件的方便之处。 如图所示:
这里有以下几种搜索方式:
keyword(关键字查询), classname(类名查询), GAV(groupId,artifactId,version组合查询), checksum(校验和查询),这里我认为前三种使用的最多。
比如,刚刚通过部署后的user-core模块,这个模块正好被其他项目人员使用到,他知道模块的名称叫user-core,这个时候,可以通过GAV的方式进行查询,如图所示:
查出来之后,只要将依赖文件复制到项目的pom.xml配置中,就可以从私有仓库中下载到其本地仓库进行使用了。
创建本地仓库并设置权限
创建仓库
在大公司的项目开发中,不可能所有的Snapshots和Releases版本都发布到Nexus中默认对应的Snapshots和Releases仓库中,我们可以给每个项目创建自己的本地仓库,并赋予相应的角色权限进行操作。比如创建一个UserRelease仓库用来存储User项目的Release版本,这个时候,操作如图所示:
接着将信息填入New Hosted Repository中,如图27所示
这个时候就创建了UserRelease的本地仓库,如图所示
创建权限
虽然我们已经创建了UserRelease仓库,但仓库的权限仅为预览,如果让其可以有增删改查的权限,只要点击图中Add,如图所示:
接着进行信息填入,如图30所示
这个时候UserRelease仓库就拥有了所有的权限,如图31所示
创建角色
虽然有了权限,但在使用的过程中,进一步创建一个角色,对角色添加相应的权限,然后在让角色中添加相应的用户,这样在发布的时候就可以达到权限的最细致化,管理起来更加方面。 如图32,创建角色
然后进行填写信息,如图33
在这里有Add按钮,点击按钮的时候进行如下操作
此时,创建角色后如图35所示:
创建用户
角色创建好后就可以创建用户,通过专有的用户来进行专有项目的部署和其他操作,如图创建用户
然后填入信息,如图所示
在图37中点击Add按钮出现图38所示的选项,如下操作
就是选择该用户所属的角色,而角色拥有所有的权限,完成后如图39所示
注:在以上操作中,我们首先创建一个本地仓库(这里是Release版本的,项目中可以在创建一个Snapshot版本,操作过程同上),然后给这个仓库赋予权限,然后将这些权限通过一个角色进行拥有,当然在这里可以创建不同的角色赋予不同的权限,这时候,创建一个用户,使得该用户担当一个角色,这样的话,这个用户就可以拥有这个角色中的权限,下面就可以按需对所在的项目使用相应的用户进行操作了。
这样根据以上情况可以建立UserSnapshot的仓库,以及将所有的权限付给不同的角色,或者可以让UserReleaseRole同样拥有UserSnapshot的所有权限,并且同样的用户拥有Release和Snapshot仓库的所有权限,这样的话如果进行部署,就可以如下进行配置,在对应部署模块的pom.xml中可以这样配置
<distributionManagement>
<repository>
<id>user-core-release</id>
<name>user core release</name>
<url>http://localhost:8081/nexus/content/repositories/UserRelease/</url>
</repository>
<snapshotRepository>
<id>user-core-snapshot</id>
<name>user core snapshot</name>
<url>http://localhost:8081/nexus/content/repositories/UserSnapshots/</url>
</snapshotRepository>
</distributionManagement>
而在settings.xml中就可以这样进行配置
<server>
<id>user-core-release</id>
<username>UserRelease</username>
<password>user123</password>
</server>
<server>
<id>user-core-snapshot</id>
<username>UserRelease</username>
<password>user123</password>
</server>
这样就可以把相应的Release和Snapshot版本部署到相应的Nexus仓库中了。