《Maven实战》阅读总结(一)Maven介绍、安装配置、入门以及坐标和依赖

(一)Maven介绍

maven:是一个跨平台的项目管理工具,主要服务于Java平台的项目构建、依赖管理和项目信息管理
项目构建:经过插件帮你完成项目的清理、编译、测试、打包、部署。
依赖管理:经过坐标从maven仓库导入java类库(jar文件)
项目信息管理:项目描述、开发者列表、版本控制系统地址、许可证、缺陷管理系统地址等。html

(二)Maven的安装和配置

下载页面:http://maven.apache.org/downl...
下载解压便可。在安装 Maven 以前,需确保安装JDK,可经过 java -version 命令查看
环境变量配置:
1) M2_HOME = maven的bin目录所在路径(不包含/bin)
2) Path 添加 %M2_HOME%/bin
maven升级:下载新版本Maven解压,修改M2_HOME路径便可
Linux 可经过符号连接简化 Maven升级java

Maven目录分析

目录 说明
bin mvn运行脚本,配置java命令, mvn / mvnDebug
boot 只包含一个plexus-classworlds jar包,Maven类加载器框架
conf settings.xml全局maven配置文件,【推荐】复制该文件至 ~/.m2/目录下(~表示用户目录)
lib Maven运行须要的Java类库,Maven jar包以及第三方依赖jar包

mvn help:system
该命令会打印出全部的Java系统属性和环境变量spring

~/.m2目录分析

~(用户目录) Winodw:C:Users用户名 或者 C:Document and Settings用户名
Linux: cd 回车

在用户目录下能够发现.m2文件夹。
默认状况下,.m2文件下只放置了Maven本地仓库repository,不过大多数Maven用户须要复制 settings.xml 到此目录下做为用户配置文件。sql

Maven插件

IDE使用Maven须要安装Maven插件,如Eclipse的m2eclipse插件,NetBeans的Maven插件数据库

Maven安装最佳实践

设置环境变量MAVEN_OPTS 一般设置MAVEN_OPTS的值为-Xms128m -Xmx512m,由于mvn命令实际执行java命令,项目较大时,使用Maven生成项目站点须要占用大量内存,若是没有该配置,容易获得java.lang.OutOfMemeryException
配置用户settings.xml 用户可选择配置conf中的全局配置或.m2目录下的用户配置,推荐使用用户配置,避免影响系统中其余用户,而且配置用户配置便于maven升级,由于Maven升级后,conf下的全局配置须要从新设置,而.m2下的用户配置不变。
不使用IDE内嵌Maven IDE集成的Maven,一般版本比较新,不稳定,其次要确保IDE中配置Maven与命令行Maven保持一致,避免版本不一样形成构建行为的不一致。

(三)Maven入门

编写POM(Project Object Model)

clipboard.png

XML头 指定xml文档版本和编码方式
project 声明POM相关的命名空间和xsd元素
modelVersion 指定当前POM模型的版本
groupId 定义了项目属于哪一个组(Maven项目隶属的实际项目),相似包名,域名反写+实际项目名,如:org.sonatype.nexus
artifactId 定义了项目在组中惟一的ID,可理解为项目名(实际项目中的一个maven项目(模块))推荐格式:实际项目名-模块名 如:nexus-indexer
version 指定了项目当前的版本
name 声明了一个对用户更为友好的项目名称,非必须,推荐声明,方便信息交流。

编写主代码

默认状况下Maven项目目录apache

|-src
  |-main
    |-java                   Java代码
      |-groupId.artifactId    定义包名为项目组.项目名,全部java类都在该包下建立
    |-resources              资源目录
  |-test                     测试目录
    |-java                   测试Java代码
      |-groupId.artifactId

项目编译命令 mvn clean compile
此命令主要执行了三个插件以及插件目标api

clean:clean 删除target目录,默认状况下,Maven构建的全部输出都在target/目录中
resource:resources 主项目资源处理
compile:compile 将项目主代码编译至target/classes目录

编写测试代码

添加POM依赖,导入Junit jar包安全

<dependencies>    <!-- 可包含多个dependency声明项目的依赖 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.7</version>
        <scope>test</scope>    <!-- 声明依赖范围,只对测试有效,在主代码中使用junit 会编译错误 -->
    </dependency>
</dependencies>

一个典型测试单元需具有三步骤:1 准备测试类及数据 2 执行要测试的方法 3 检查结果
在测试包中建立测试类(类名不要为Test),
编写测试方法(void无返回值 @Test注释),框架

项目测试命令:mvn clean test
此命令主要执行了五个插件以及插件目标eclipse

clean:clean 项目清理
resource:resources 项目主资源处理
compile:compile 主代码编译
resource:testResource 测试资源处理
compile:testCompile 测试代码编译

声明项目编译JDK版本

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compile-plugin</artifactId>
        <configuration>
            <source>1.8</source>
            <target>1.8</target>
        </configuration>
    </plugin>
</plugins>

打包和运行

mvn clean package
该命令会在编译、测试后执行 jar:jar
jar:jar任务负责打包,jar插件的jar项目在tartget目录将主代码打包成jar包,根据artifact-version.jar(项目名-版本号.jar)命名,也可经过<build>标签中的<finalName>子标签来自定义

mvn clean install 其余Maven项目依赖此项目时,需将jar包安装到Maven本地仓库中
该命令会在编译、测试、打包后执行install:install
install:install任务将jar包安装到本地仓库repository下,规则为groupIdartifactIdversionartifactId-version.jar

可执行jar包须要设置 META-INF/MANIFEST.MF 文件中编辑 Main-Class 一行。
也可经过配置maven-shade-plugin 或 maven-jar-plugin 等插件实现

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <classesDirectory>target/classes/</classesDirectory>
        <archive>
            <manifest>
                <!-- 主函数的入口 -->
                <mainClass>cn.roylion.snake.Snake</mainClass>
                <!-- 打包时 MANIFEST.MF文件不记录的时间戳版本 -->
                <useUniqueVersions>false</useUniqueVersions>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
            </manifest>
            <manifestEntries>
                <Class-Path>.</Class-Path>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

Archetype项目骨架

在项目的根目录中放置pom.xml,在src/main/java中放置项目的主代码,在src/test/java中放置项目的测试代码。
咱们称这些基本的目录结构和pom.xml文件内容为项目骨架,Archetype能够帮助咱们快速勾勒出项目骨架。

运行插件maven-archetype-plugin生成项目骨架:
格式:mvn groupId:artifactId:version:goal
mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-5:generate
简化格式:mvn archetype:generate (maven2中是不安全,会自动下载最新的版本,可能获得不稳定的SNAPSHOT版本)

执行插件以后会列出不少可用的Archetype列表,每个Archetype前面都会对应有一个编号,同时命令行会提示一个默认的编号,其对应的Archetype为maven-archetype-quickstart,
直接回车以选择该Archetype,或输入指定编号回车
紧接着会Maven会提示输入要建立项目的groupId、artifactId、version以及包名package,确认后便可生成一个maven项目。

(四)坐标和依赖

maven坐标

Maven定义了一组规则,世界上任何一个构件均可以使用Maven坐标惟一标识,
Maven坐标的元素包括 groupId、artifactId、version、packaging、classifier
中央仓库:Maven经过坐标从中央仓库寻找相应的构件供咱们使用。

groupId 定义当前Maven项目隶属的实际项目。由于Maven的模块概念,Maven项目和实际项目不必定是一对一关系,一个实际项目每每会被划分红不少模块。若是groupId只定义到组织级别,artifactId只能定义Maven项目(模块),则实际项目这一层将难以定义,最后,groupId的表现形式相似包名,域名反写+实际项目名,如:org.sonatype.nexus
artifactId 定义实际项目中的一个Maven项目(模块),推荐使用实际项目名做为artifactId的前缀,如nexus-indexer
version 定义Maven项目当前所处的版本。Maven定义了一套完整的版本规范,以及快照(SNAPSHOT)概念。
packaging 定义Maven项目的打包方式,未定义时,默认为jar
classifier 帮助定义构建输出的一些附属构件。经过使用插件生成如nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-resource.jar这样一些附属构件,其中包含java文档和源代码,此时,javadoc和resource就是这两个附属构件的classifier。这样,附属构件就拥有本身惟一的坐标,

上述5个元素中,groupId、artifactId、version是必须定义的,packaging是可选的(默认为jar),而classifier是不能直接定义的。
项目构件的文件名规则:artifactId-version[-classifier].packaging
maven仓库的布局也是基于maven坐标的

maven依赖管理配置

<project>
...
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...<optional>
            <exclustion>
                ...
            </exclustion>
        </dependency>
        ...
    </dependencies>
...
</project>

根元素project下的dependencies能够包含多个dependency元素,以声明一个或多个项目依赖

groupId、artifactId、version 依赖的基本坐标,Maven根据坐标才能找到须要的依赖
type 依赖的类型,对应于项目坐标定义的packaing,默认为jar
scope 依赖的范围
optional 标记依赖是否可选
exclusions 排除传递性依赖

依赖范围

首先,Maven在编译项目主代码时须要使用一套classpath(编译classpath);
其次,Maven在编译和执行测试的时候会使用另一套classpath(测试classpath);
最后,实际运行Maven项目的时候,又会使用另一套classpath(运行classpath)。
依赖范围:maven须要使用三套classpath,控制依赖与三种classpath(编译classpath、测试classpath、运行classpath)的关系,Maven有如下几种依赖范围。

类型 有效范围 说明
compile 编译、测试、运行 编译依赖范围,若是没有指定,默认使用该范围,如 spring-core
test 测试 测试依赖范围,如 junit
provided 编译、测试 已提供依赖范围,如 servlet-api
runtime 测试、运行 运行时依赖范围,如JDBC驱动实现
system 编译、测试 系统依赖范围,依赖关系与provided一致,可是,使用system范围依赖,必须经过systemPath元素显式指定依赖文件的路径。因为此类依赖不经过Maven仓库,可能会形成构建的不可移植,需谨慎使用,systemPath可引用环境变量。
<dependency>
  <groupId>javax.sql</groupId>
   <artifactId>jdbc-stdext</artifactId>
   <version>2.0</version>
   <scope></scope>
   <systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
import 导入依赖范围,该依赖范围不会对三种classpath产生实际的影响。

传递性依赖

假设:A->B->C
当项目A依赖于B包,而B包又依赖于C包,故须要引入C包。
使用Maven只须要引入B包便可,C包由Maven经过传递性依赖引入。
Maven经过传递性依赖机制,解析各个直接依赖的POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。

传递性依赖与依赖范围

假设:A->B->C
A依赖B -> 第一直接依赖 依赖范围
B依赖C -> 第二直接依赖 依赖范围
A依赖C -> 传递性依赖 依赖范围 由第一直接依赖和第二直接依赖决定,以下图
clipboard.png
总结:
第二直接依赖范围是compile,传递性依赖范围与第一直接依赖范围一致;
第二直接依赖范围是test,依赖不得以传递;
第二直接依赖范围是provided,只第一直接依赖范围也为provided时,传递性范围一样为provided;
第二直接依赖范围是runtime,除第一直接依赖范围为compile时为runtime,传递性依赖范围与第一直接依赖范围一致。

依赖调解

假设:A->B->C-X(1.0)    A->D->X(2.0)
传递性依赖机制引入不通版本的X时,发生依赖重复,Maven遵循的第一原则:路径最近者优先。
该例中X(1.0)路径长度为3,X(2.0)路径长度为2。所以X(2.0)会被解析使用
当路径长度相同时,Maven(2.0.9以后)定义第二原则:第一声明者优先。

可选依赖(optional 标记依赖是否可选)

假设:A->B , B->X(可选) , B->Y(可选)
若是全部这个三个依赖的范围都是compile,那么X,Y就是A的compile范围传递性依赖,可是,因为X,Y是可选依赖,依赖将不得以传递。

可选依赖的使用场景:项目B具备两个特性,特性一依赖于X,特性二依赖于Y,而且这两个特性是互斥的,用户没法同时使用。
好比B是一个持久层隔离工具包,它支持多个数据库,包括MySql(假设驱动依赖X)、Oracle(假设驱动依赖Y),
在构建项目B时,须要引入这两种数据库的驱动程序,但在引入项目B时,只会依赖一种数据库,
此时项目B构建时需声明依赖X,Y可选(<scope>true</scope>),
当项目A依赖项目B时,X,Y项目依赖不会被传递,同时项目A根据实际需求,显式声明X或Y依赖(如项目使用MySql数据库时,项目A依赖项目B,同时显式声明X依赖)

说明:在理想状态下,是不该该使用可选依赖。可选依赖是基于一个项目实现多个特性,违背了面向对象设计中的单一职责性原则。

排除依赖

假设:A->B-X(1.0)
传递性依赖会隐式地引入不少依赖,如依赖X(1.0),这极大地简化项目依赖管理,但这种特性也会带来一些问题。
例如,我须要依赖X(2.0)时,可使用排除依赖排除X(1.0),同时显式声明X(2.0)依赖。
代码中使用exclusions元素声明排除依赖,exclusions能够包含一个或多个exclusion子元素,所以能够排除一个或多个传递性依赖。
注意:声明exclusion时只须要groupId和artifactId,不须要version。由于Maven解析后的依赖中,不会存在groupId和artifactId相同,而version不一样的两个依赖(依赖调解特性)。

归类依赖

引入同一项目的不一样模块时,这些依赖的版本都是相同的,如Spring Framework
这里简单用到了Maven属性,首先使用properties元素声明Maven属性(定义版本),并在dependency声明中引用这个版本(${})。

<project>
    ...
    <properties>
        <springframework.version>2.5.6</springframework.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        ...
    </dependencies>
    ...
</project>

优化依赖

了解Maven项目的依赖,去除多余的依赖,显式地声明某些必要的依赖

Maven会自动解析全部项目的直接依赖和传递性依赖,而且根据规则正确判断每一个依赖的范围,对于一些依赖冲突,也能进行调节,以确保一个构件只有惟一的版本在依赖中存在。在这些工做以后,最后获得的那些依赖被称为已解析依赖。
mvn dependency:list 查看已解析依赖列表及其依赖范围

在此基础上,还能进一步了解已解析依赖的信息,将直接在当前项目POM声明的依赖定义为顶层依赖,而这些顶层依赖的依赖则定义为第二层依赖,以此类推,有第3、第四层依赖。当这些依赖经解析后,就会构成一个依赖树。经过这个依赖树就能清楚得看到某个依赖是经过哪条传递路径引入的。
mvn dependency:tree 查看当前项目的依赖树

mvn dependency:analyze 帮助分析当前项目的依赖
经过分析依赖树,能够获得两个重要结果:
1 Used undeclared dependencies:意指项目中使用到的,可是没有显式声明的依赖。(存在隐患)
假设:A->B->C
若是项目A->C时,而且项目A并无显示声明C依赖时,而使用B的传递依赖C时,当升级直接依赖B,传递依赖C也可能发送变化,而且这种变化不易发现,致使当前项目出错。如接口改变,致使编译失败。所以,显式声明任何项目中直接用到的依赖。
2 Unused declared dependencies:意指项目中未使用的,但显式声明的依赖。
对于这一类依赖,不要简单的直接删除其声明,而是应该仔细分析,由于dependency:analyze工具只会分析编译主代码和测试代码须要用到的依赖,一些执行测试和容许时须要的依赖它就发现不了。

相关文章
相关标签/搜索