【Maven】---操作指南
Maven坐标与依赖
最近想深度学习下maven,找到一本书叫《Maven实战》,这本书讲的确实很好,唯一遗憾的是当时maven教学版本是3.0.0的,而目前已经到了3.5.4了,版本存在差距,
没关系,如果有时间和精力我也会阅读官方文档,看看到底有哪些变换。
一、坐标详解
1、何为Maven坐标
maven定义了这样一组规则:世界上任何一个构件都可以使用maven坐标唯一标识,坐标元素包括:groupId、artifactId、version、packaging、classifier。只要提供正确
的坐标就能从仓库中找到相应的构件供我们使用。maven从哪里下载构件呢?答:maven内置了一个中央仓库的地址,该中央仓库包含了世界上大部分流行的开源构件。
2、坐标详解
任何构件都必须明确定义自己的坐标,而一组maven坐标是通过一些元素定义的,他们是:groupId、artifactId、version、packaging、classifier。先看一组坐标定义如下
<groupId>org.sonatype.nexus</groupId> <artifactId>nexus-indexer</artifactId> <version>2.0.0</version> <packaging>jar</packaging>
这是nexus-indexer项目的坐标,nexus-indexer是一个对maven仓库编纂索引并提供搜索功能的类库,它是Nexus项目的一个子模块。
goupId: 定义了当前maven项目隶属的实际项目,一般是域名+项目名。比如:com.alibaba.taotao
artifactId:该元素定义实际项目中的一个maven项目(模块,一般推荐项目名+子模块名。比如:taobao-web
version: 定义maven项目当前所处版本。
packaging:定义maven项目的打包方式,默认使用jar。
classifier:该元素用来帮助定义构件输出的一些附属构件。
上述5元素groupId、artifactId、version是必须的,packaging可选,默认jar,classifier不能直接定义。同时,项目构件的文件名是与坐标对应的,
一般的规则为artificatId-version[-classifier].packaging。packing并非一定与构件扩展名对应,比如packing为maven-plugin的构件扩展名为jar。
二、依赖详解
1、依赖的配置
一般的依赖只有基本的 groupId,artifactId,version。我们来看下详细的依赖配置
<project> <dependencies> ... <dependency> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <type>...</type> <scope>...</scope> <optional>...</optional> <exclusions> <exclusion> ... </exclusion> </exclusions> </dependency> ... </dependencies> </project>
gourpId、artifactId、version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,maven根据坐标才能找到需要的依赖。
type:依赖的类型,对于项目坐标定义的packing,大部分情况是不必声明,默认是jar。
scope :依赖的范围 下面具体讲解
optional: 标记依赖是否可选,值为true或false,默认为false, 如果为可选依赖,则依赖不具有传递性。即B->X(可选依赖),A->B。此时A的依赖中不包含X。
exclusions:用来排除传递性依赖。
大部分依赖声明只包含基本坐标,然而在一些特殊情况下,其他元素至关重要。
2、依赖范围scope
classpath:用于指定.class文件存放的位置,类加载器会从该路径中加载所需的.class文件到内存中。maven在编译、执行测试、实际运行有着三套不同的classpath。
编译classpath:编译主代码有效
测试classpath:编译、运行测试代码有效
运行classpath:项目运行时有效
maven的依赖范围
compile:编译依赖范围。(默认方式),有效范围:编译classpath+测试classpath+运行classpath。
test:测试依赖范围。有效范围:测试classpath 比如:JUnit,只在测试时使用,在编译主代码和运行时不需要此依赖。
provided:已提供依赖范围。有效范围:编译classpath+测试classpath。
runtime:运行时依赖范围。有效范围:测试classpath+运行classpath。比如:JDBC驱动实现(mysql-connector-java)。
system:系统依赖范围。有效范围:编译classpath+测试classpath。使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径,
因为此类依赖不是通过maven仓库解析的,而且往往与本地及其系统绑定,可能造成构建的不可移植,慎用。systemPath元素可以引用环境变量。
<!--你引用本地的jar包,当然只能本地使用了--> <dependency> <groupId>javax.sql</groupId> <artifactId>jdbc-stdext</artifactId> <version>2.0</version> <scope>system</scope> <systemPath>${JAVA_HOME}/lib/rt.jar</systemPath> </dependency> <!--你会发现这里这里是test,那么里面有个@Test 注解 只能在test目录下有效,在main目录下该注解是无效的--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
3、传递依赖性
假设A依赖B,B依赖C,则A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。
如下表,最左边是第一直接依赖范围,上面是第二直接依赖范围,中间交叉单元格表示传递性依赖范围。
规律:
第二直接依赖范围是compile时,传递依赖的范围与第一直接依赖范围一致。
第二直接依赖范围是test时,依赖不会传递。
第二直接依赖范围是provided时,只传递第一直接依赖为provided的依赖。
第二直接依赖范围是runtime时,传递依赖的范围与第一直接依赖范围一致,但是compile例外,此时传递依赖范围为runtime。
4、依赖调节
Maven引入的传递性依赖机制,一方面大大简化和方便了依赖声明,另一方面,大部 分情况下我们只需要关心项0的直接依赖是什么,而不用考虑这些直接依赖会引人什么传 递性依赖。
但有时候,当传递性依赖造成问题的时候,我们就需要清楚地知道该传递性依 赖是从哪条依赖路径引入的。
例如,项目A有这样的依赖关系:A->B->C->X(1.0)、A->D->X(2.0),X是A的 传递性依赖,但是两条依赖路径上有两个版本的X,那么哪个X会被Maven解析使用呢?
两个版本都被解析显然是不对的,因为那会造成依赖重复,因此必须选择一个。Maven依 赖调解(Dependency Mediation)的第一原则是:
路径最近者优先。该例中X( 1.0)的路径氏 度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解折使用。
依赖调解第一原则不能解决所有问题,比如这样的依赖关系:A->B->Y(1.0)、A-> C->Y(2.0),Y(1.0)和Y(2.0)的依赖路径长度是一样的,都为2。Maven定义了依赖调解的第二原则:
第一声明者优先。在依 赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最前的那个依赖优胜。该例中,如果B的依赖声明在C之前,那么Y (1.0)就会被解析使用.
5、排除依赖
传递性依赖会给项目引入很多依赖,简化项目依赖管理,但是也会带来问题。
需求:比如当前项目有一个第三方依赖,而第三方依赖依赖了另一个类库的SNAPSHOT版本,那么这个SNAPSHOT就会成为当前项目的传递性依赖,而SNAPSHOT的不稳定性会直接影响到当前项目。
这时候就应该排除掉SNAPSHOT。并且声明该类库的正式发布版本。
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>3.6.10.Final</version> <exclusions> <exclusion> <groupId>slf4j-api</groupId> <artifactId>slf4j-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency>
上述代码中hibernate-validator依赖slf4j-api,但是当前项目不想使用传递过来的slf4j-api,所以通过exclusions声明排除依赖。当前项目声明了自己需要的1.7.12版本的slf4j-api。
exclusions可以包含一个或者多fexdWon子元素,因此可以排除一个或者多 个传递性依赖。需要注意的是,声明exclusion的时候只需要groupld和artifactld,而不 笛要version元素,
这是因为只需要gmupkl和arlifactid就能唯一定位依赖图中的某个依 赖。换句话说,Maven解析后的依赖中,不可能出现groupW和artifactld相同,但是 version不同的两个依赖。
注意:排除依赖的时候也要注意,比如A依赖1.1版本的B, ,而你不想要1.1版本的B,而是要2.1的B,这个时候也需要考虑A跟2.1的B是否能兼容。
6、归类依赖
需求:关于springframework的依赖有好多,org.springframework:spirng-core:2.5.6、org.springframework:sprng-beans:2.5.6、org.springframework:spring-context:2.5.6,
他们来自同一个项目不同模块,因此版本都是相同的,可以预见在升级spring时这些依赖都会一起升级,为了方便统一所以使用properties元素定义maven属性。
<project> .... <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <springframework.version>4.3.2.RELEASE</springframework.version> </properties> <!--通过使用${springframework.version}替换掉实际值,将所有spring依赖的版本值都使用这一引用值表示。--> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframework.version}</version> </dependency> </dependencies> ... </project>
Linux搭建Nexus3.X私服
备注:linux版本: ubuntu 同时已经部署好JDK8环境
一、linux安装nexus
1、创建文件夹并进入该目录
cd /usr/local && mkdir nexus && cd nexus
2、下载nexus安装包
wget http://sonatype-download.global.ssl.fastly.net/nexus/3/nexus-3.6.0-02-unix.tar.gz
3、解压、重命名
tar -zxvf nexus-3.6.0-02-unix.tar.gz //解压 mv nexus-3.6.0-02 nexus //重命名
4、自定义配置虚拟机可打开 nexus.vmoptions 文件进行配置
如果Linux硬件配置比较低的话,建议修改为合适的大小,否则会出现运行崩溃的现象
# vim nexus/bin/nexus.vmoptions //虚拟机选项配置文件
5、启动nexus
启动 Nexus(默认端口是8081),Nexus 常用的一些命令包括:/usr/local/nexus/nexus/bin/nexus {start|stop|run|run-redirect|status|restart|force-reload},下面我们启动Nexus:
启动命令:bin/nexus start
说明已经安装启动成功,上面报错意思是nexus建议不要用root用户来启动nexus,但并不影响启动,如果要去除这个可以百度。
6、其它说明
1)Nexus默认的端口是8081,可以在etc/nexus-default.properties配置中修改。
2)Nexus默认的用户名密码是admin/admin123
3)当遇到奇怪问题时,重启nexus,启动时间会比较慢 要1分钟左右后才能访问。
4)Nexus的工作目录是sonatype-work(路径一般在nexus同级目录下)
二、仓库介绍
当访问:http://ip:8081/ 会有nexus界面
用户登陆,进入Repositories页面点击Nexus“Log in”,输入默认用户名(admin)和默认密码(admin123)登录。后找到Repositories
1、四种仓库类型介绍
默认仓库介绍
1)maven-central: maven中央库,默认从https://repo1.maven.org/maven2/拉取jar
2)maven-releases: 私库发行版jar
3)maven-snapshots:私库快照(调试版本)jar
4)maven-public: 仓库分组,把上面三个仓库组合在一起对外提供服务,在本地maven基础配置settings.xml中使用。
Nexus默认的仓库类型有以下四种:(上面的名字可以随便取,关键是它对应的是什么仓库类型)
1)group(仓库组类型):又叫组仓库,用于方便开发人员自己设定的仓库;
2)hosted(宿主类型):内部项目的发布仓库(内部开发人员,发布上去存放的仓库);
3)proxy(代理类型): 从远程中央仓库中寻找数据的仓库(可以点击对应的仓库的Configuration页签下Remote Storage Location属性的值即被代理的远程仓库的路径);
4)virtual(虚拟类型): 虚拟仓库(这个基本用不到,重点关注上面三个仓库的使用);
Policy(策略):表示该仓库为发布(Release)版本仓库还是快照(Snapshot)版本仓库;
2、仓库拉取jar包流程
1)Maven可直接从宿主仓库下载构件,也可以从代理仓库下载构件,而代理仓库间接的从远程仓库下载并缓存构件
2)为了方便,Maven可以从仓库组下载构件,而仓库组并没有时间的内容(下图中用虚线表示,它会转向包含的宿主仓库或者代理仓库获得实际构件的内容).
二、创建新用户和新仓库
1、创建用户
登陆admin后,可以点击上面的“设置”图标,在“设置”里可以添加用户、角色,对接LDAP等的设置,如下:
创建用户好后,重新登陆创建的用户
2、创建仓库
上面对仓库的概率清楚后,我们开始创建新仓库,主要创建3个仓库
proxy仓库 作用是去远程拉取jar包
hosted仓库 作用是存放本地上传的三方jar包
group仓库 作用是将上面来个放到这个组里,进行统一管理
(1)proxy 代理仓库创建
中央仓库的代理默认为https://repo1.maven.org/maven2/ 我们可以更换成阿里云中央仓库。
(2)hosted 仓库创建
host仓库这里了两种不同的存储类型
(3)group仓库
主要就是把上面三个仓库放到该组里,然后让这个组给外面访问。
全家福来一个
有关在linux服务器上搭建nexus的工作已经完成,接下来就需要配置maven相关配置文件,来测试该仓库的是否成功,下一篇博客详细写配置信息。
上一遍博客已经在linux服务器上,搭建好nexus私服了,博客地址:Linux搭建Nexus3.X私服
现在就需要配置setting.xml
和pom.xml
来使nexus作为maven的私服。setting.xml文件在conf下面,pom.xml是在你创建maven项目中的pom.xml中。
一、将jar发送到nexus私服务器
1、创建maven项目
创建一个最简单的maven项目,然后新建一个工具类,用来测试当把它打成jar包放到私服后,其它项目是否能够成功引用。
2、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>com.jincou</groupId>
<artifactId>xuxiaoxiao</artifactId>
<!--SNAPSHOT代表是快照版本-->
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>xuxiaoxiao</name>
<description>Demo project</description>
<distributionManagement>
<repository>
<!--id的名字可以任意取,但是在setting文件中的属性<server>的ID与这里一致-->
<id>releases</id>
<!--指向仓库类型为host(宿主仓库)的储存类型为Release的仓库-->
<url>http://47.96.4.110:8081/repository/java-release/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<!--指向仓库类型为host(宿主仓库)的储存类型为Snapshot的仓库-->
<url>http://47.96.4.110:8081/repository/java-snapshot/</url>
</snapshotRepository>
</distributionManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
</project>
3、setting.xml配置
在这里只要配置登陆nexus的用户名密码,不然没有用户名和密码怎么能将jar包发送到私服呢。
<!--此处设置的用户名和密码都是nexus的登陆配置-->
<servers>
<server>
<id>releases</id> <!--对应pom.xml的id=releases的仓库-->
<username>xuxiaoxiao</username>
<password>xuxiaoxiao123</password>
</server>
<server>
<id>snapshots</id> <!--对应pom.xml中id=snapshots的仓库-->
<username>xuxiaoxiao</username>
<password>xuxiaoxiao123</password>
</server>
</servers>
注意
: maven会判断版本后面是否带了-SNAPSHOT,如果带了就发布到snapshots仓库,否则发布到release仓库。这里我们可以在pom.xml文件中
执行命令:mvn deploy
发现部署到nexus私服成功,我们到私服查看下,因为这里的版本是带SNAPSHOT,所以会发布到snapshots仓库中。
说明已经成功将jar包发布到nexus私服中了。那么下一步是如何引用私服中的jar包了。
二、从nexus引用第三方jar包
让maven项目使用nexus作为远程仓库有两种方式,第一种
是在项目的pom.xml中进行更改,让单个项目使用nexus仓库;另一种
是通过修改maven的配置文件settings.xml进行更改,让所有项目都使用nexus仓库。我们这里采取第二种,只需要setting.xml就可以了。还有就是拉取jar的私服仓库地址只要写一个java-group就可以了,因为在创建这个组的时候,里面已经包含了其它三个仓库。
1、setting.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">
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
<!--第一个nexus-xu要和下面的mirror中的id一致,代表拉取是也需要进行身份校验-->
<server>
<id>nexus-xu</id>
<username>xuxiaoxiao</username>
<password>xuxiaoxiao113</password>
</server>
<server>
<!--这两个前面讲过,是jar上传时候进行的验证,id对应的是pom中id属性的值-->
<id>releases</id>
<username>xuxiaoxiao</username>
<password>xuxiaoxiao113</password>
</server>
<server>
<id>snapshots</id>
<username>xuxiaoxiao</username>
<password>xuxiaoxiao113</password>
</server>
</servers>
<mirrors>
<mirror>
<id>nexus-xu</id>
<name>internal nexus repository</name>
<!--镜像采用配置好的组的地址-->
<url>http://47.96.44.110:8081/repository/java-group/</url>
<mirrorOf>!internal.repo,*</mirrorOf>
</mirror>
</mirrors>
<profiles>
<profile>
<!--ID用来确定该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>
<profile>
<id>nexus-pr</id>
<!-- 远程仓库列表 -->
<repositories>
<repository>
<id>nexus-xu</id>
<name>Nexus Central</name>
<!-- 虚拟的URL形式,指向镜像的URL-->
<url>http://47.96.44.110:8081/repository/java-group/</url>
<layout>default</layout>
<!-- 表示可以从这个仓库下载releases版本的构件-->
<releases>
<enabled>true</enabled>
</releases>
<!-- 表示可以从这个仓库下载snapshot版本的构件 -->
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<!-- 插件仓库列表 -->
<pluginRepositories>
<pluginRepository>
<id>nexus-xu</id>
<name>Nexus Central</name>
<url>http://47.96.44.110:8081/repository/java-group/</url>
<layout>default</layout>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<!--需要激活 <profile>中的ID才生效-->
<activeProfile>nexus-pr</activeProfile>
<activeProfile>jdk-1.8</activeProfile>
</activeProfiles>
</