Maven入门介绍

来源:互联网 发布:selenium java client 编辑:程序博客网 时间:2024/06/11 09:09

导语

最近在学习Java了,以后分享的文章主要就以Java为主了,偶尔也会分享一下Objective-C方面的文章,这篇读书笔记主要介绍了Maven的一些核心概念和常用的一些插件。

Maven

什么是Maven?

什么是Maven呢?我们看下官网给出的一段介绍:

Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information.

从介绍中我们可以看到Apache Maven是一个项目管理和理解工具,它基于项目对象模型(POM)的概念,它可以管理项目的构建、报告和文档。我们也会经常听到有人对Maven是这样理解的:

  • Maven是一个站点和文档工具。
  • Maven扩展Ant,让你下载依赖关系。
  • Maven是一组可重用的Ant脚本。

这几个方面都是Maven的一些功能,Maven所提供的功能远比这强大的许多,不过对于我们在日常项目当中用到最多的就是管理第三方库的依赖、项目的构建。如果开发工具使用的是IntelliJ IDEA,自动就帮你安装好了Maven,不是的话,也可以参考下官方给出的Maven安装教程,也是比较简单的。接下来看下怎样用Maven来满足我们项目的日常需要。

约定优于配置

Maven使用约定优于配置的原则,如下所示:

目录 目的 ${basedir} 存放pom.xml和所有的子目录 ${basedir}/src/main/java 项目的java源代码 ${basedir}/src/main/resources 项目的资源,比如说property文件,springmvc.xml ${basedir}/src/test/java 项目的测试类,比如说Junit代码 ${basedir}/src/test/resources 测试用用的资源 ${basedir}/src/main/webapp/WEB-INF web应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面 ${basedir}/target 打包输出目录 ${basedir}/target/classes 编译输出目录 ${basedir}/target/test-classes 测试编译输出目录 Test.java Maven只会自动运行符合该命名规则的测试类 ~/.m2/repository Maven默认的本地仓库目录位置

一个maven项目在默认情况下会产生jar文件,另外,编译后的classes会放在basedir/target/classesjar{basedir}/target下面。使用约定优于配置带来最大的好处就是项目的统一,任何人在使用Maven项目的时候,文件的存放位置都是一样的,通用性比较好。这也是为什么我们在用intellij创建一个maven项目的时候,需要配置源文件、资源文件路径的原因。如下图就是常用的几个配置:

maven 1-1.png

Maven的几个核心概念

POM(Project Object Model)

一个项目所有的配置都放在POM文件中:定义项目的类型、名字、管理依赖关系,定制插件的行为等等。看下我自己写的小demo中pom中配置如下:

<groupId>com.dodonew</groupId><artifactId>springmvc</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>springmvc</name><url>http://maven.apache.org</url><dependencies>    <!-- spring日志依赖 -->    <dependency>        <groupId>org.slf4j</groupId>        <artifactId>jcl-over-slf4j</artifactId>        <version>1.7.21</version>    </dependency>    <dependency>        <groupId>ch.qos.logback</groupId>        <artifactId>logback-classic</artifactId>        <version>1.1.7</version>    </dependency></dependencies>

在POM中,groupId、artifactId、packaging、version叫做maven坐标,它能唯一的确定一个项目。有了maven坐标,我们就可以用它来指定我们的项目所依赖的其他项目、插件、或者父项目。我写的demo很简单,但是比较大的项目一般会分成几个子项目,在这种情况下,每个子项目就会有自己的POM文件,它们会有一个共同的父项目。这样只要构建父项目就能够构建所有的子项目了。同时子项目的POM会继承父项目的POM。关于多个子项目具体怎么操作,在文章后面会有回答的。

Maven插件

Maven常用的插件比如compiler插件、surefire插件、jar插件。比如说jar插件包含建立jar文件的目标,compiler插件包含编译源代码和单元测试代码的目标,surefire插件则是运行单元测试的目标。为什么需要这些插件呢?因为maven本身不会做太多的事情,它不知道怎么样编译或者怎么样打包。它把构建的任务交给插件去做。插件定义了常用的构建逻辑,能够被重复利用。这样做的好处是,一旦插件有了更新,那么所有maven用户都能得到更新。

Maven生命周期

生命周期指项目的构建过程,它包含了一系列的有序的阶段,而一个阶段就是构建过程中的一个步骤,比如package阶段、compiler阶段等。那么生命周期阶段和上面说的插件目标之间是什么关系呢?插件目标可以绑定到生命周期阶段上,一个生命周期可以绑定多个插件目标。当maven在构建过程中逐步的通过每个阶段时,会执行该阶段所有的插件目标。目前生命周期阶段有clean、vavidate、compiler、test、package、verify、install、site、deploy阶段。

Maven依赖管理

我们能够通过maven坐标确定一个项目,换句话说,我们可以用它来解决依赖关系。在POM中,依赖关系是在dependencies部分中定义的。比如对slf4j库和logback库的依赖关系如下:

<dependencies>    <!-- spring日志依赖 -->    <dependency>        <groupId>org.slf4j</groupId>        <artifactId>jcl-over-slf4j</artifactId>        <version>1.7.21</version>    </dependency>    <dependency>        <groupId>ch.qos.logback</groupId>        <artifactId>logback-classic</artifactId>        <version>1.1.7</version>    </dependency></dependencies>

这个依赖关系是比较简单的,但是实际开发中我们会有复杂多的依赖关系,因为被依赖的jar文件会有自己的依赖关系,那么我们是不是也得把间接依赖的jar文件也都定义在POM中呢?答案是不需要,因为maven提供了传递依赖的特性,会把依赖的jar文件它所依赖的库也自动添加进来。比如spring-webmvc库,这个库自己又依赖spring-core、spring-beans、spring-web、spring-expression、spring-context,因为依赖传递性,我们在使用spring-webmvc的时候,只需要声明对spring-webmvc的依赖关系即可,其他的库会自动帮你引入的。之所以能实现依赖传递性关键就在于下载spring-webmvc库,同时也下载了pom文件,在pom文件定义了这个库需要的依赖关系。

scope决定了依赖关系的适用范围,比如junit的scope是test,那么它只会在执行compiler:testCompile和surefire:test目标的时候才会被加载到classpath中,在执行compiler:compile目标时是拿不到junit的。换句话说就是在maven哪个生命周期中起作用了。scope的默认值是compile,即任何时候都会被包含在classpath中,在打包的时候也会被包括进去。

Maven库

我们所依赖的库是从maven默认的远程库(http://repo.maven.org/maven2)下载的,这个是公有的库。有时公司自己封装了一些私有库,这个时候我们就可以搭建自己的私有库了。本地库是指maven下载了插件或者jar文件后存放在本地机器上的拷贝。在Mac上,它的位置在~/.m2/repository。当maven查找需要的jar文件时,它会先在本地库中查找,只有在找不到的情况下,才会去远程库中找的。由于maven默认的远程库服务器在国外,国内访问的时候比较慢,建议替换成阿里云的镜像,仓库地址如下:

http://maven.aliyun.com/nexus/content/groups/public/

Maven多模块项目POM注意的事项

denpendencyManagement

在项目开发过程中,有时一个项目下面包含了几个子模块,在多模块的情况,POM的配置应该要注意写什么呢?我们通过一个例子来说明下。
有这样一个工程,里面有A模块、B模块和C模块,A模块需要引入junit和log4j库,配置如下:

<dependency>    <groupId>junit</groupId>    <artifactid>junit</artifactId>    <version>3.8.2</version></dependency><dependency>    <groupId>log4j</groupId>    <artifactid>log4j</artifactId>    <version>1.2.9</version></dependency>

此时B模块也需要引入这两个库,配置如下:

<dependency>    <groupId>junit</groupId>    <artifactid>junit</artifactId>    <version>4.8.2</version>    <scope>test</scope></dependency><dependency>    <groupId>log4j</groupId>    <artifactid>log4j</artifactId>    <version>1.2.16</version></dependency>

会发现A模块和B模块对junit和log4j库依赖的版本是不同的,出现这种情况是十分危险的,因为依赖不同版本的库可能会造成很多未知的风险。怎么解决不同模块之间对同一个库的依赖版本一样呢?Maven提供了优雅的解决办法,使用继承机制以及dependencyManagement元素来解决这个问题。如果你在父模块中配置dependencies,那么所有的子模块都自动继承,不仅达到了依赖一致的目的,还省了大段的代码,但这样来做会存在问题的。比如B模块需要spring-aop模块,但是C模块不需要spring-aop模块,如果用dependencies在父类中统一配置,C模块中也会包含有spring-aop模块,不符合我们的要求。但是用dependencyManagement就没有这样的问题。dependencyManagement只会影响现有依赖的配置,但不会引入依赖。这样我们在父模块中的配置可以更改如下所示:

<!-- dependencyManagement只会影响现有依赖的配置,但不会引入依赖。 --><dependencyManagement>    <dependencies>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.12</version>            <scope>test</scope>        </dependency>        <dependency>            <groupId>log4j</groupId>            <artifactId>log4j</artifactId>            <version>1.2.17</version>        </dependency>    </dependencies></dependencyManagement>

这段配置不会给任何子模块引入依赖,如果某个子模块需要junit和log4j,只需要这样配置即可:

<dependencies>    <dependency>        <groupId>junit</groupId>        <artifactId>junit</artifactId>    </dependency>    <dependency>        <groupId>log4j</groupId>        <artifactId>log4j</artifactId>    </dependency></dependencies>

在多模块Maven项目中,使用dependencyManagement能够有效地帮我们维护依赖一致性。

pluginManagement

上面介绍了在多模块中对依赖库的管理,接下来介绍下对插件的管理。与dependencyManagement类似,我们可以使用pluginManagement元素管理插件。一个常见的用法就是我们希望项目所有模块的使用compiler插件的时候,都是用java1.7,以及指定Java源文件编码为UTF-8,这时可以在父模块的POM中如下配置pluginManagement:

<build>    <pluginManagement>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-compiler-plugin</artifactId>                <version>2.3.2</version>                <configuration>                <source>1.5</source>                <target>1.5</target>                <encoding>UTF-8</encoding>                </configuration>            </plugin>        </plugins>    </pluginManagement></build>

这段配置会被应用到所有子模块的compiler插件中,因为Maven内置了与compiler插件与生命周期的绑定,因此子模块不需要任何maven-compiler-plugin的配置了。

Maven常用的几个插件

Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成。下面说几个常用的插件:

maven-compiler-plugin(编译插件)

用来编译Java代码,在对Java代码进行编译的时候,可以指定使用哪个JDK的版本来进行编译,配置如下所示:

<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-compiler-plugin</artifactId>    <version>3.6.0</version>    <configuration>        <source>1.7</source> <!-- 源代码使用jdk1.7 -->        <target>1.7</target> <!-- 使用jvm1.7编译目标代码 -->    </configuration>

maven-resources-plugin(资源插件)

Maven区别对待Java代码和资源文件,maven-resources-plugin则用来处理资源文件。默认的主资源文件目录是src/main/resources,很多时候会需要添加额外的资源文件目录,这个时候就可以通过配置maven-resources-plugin来实现,配置如下所示:

<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-resources-plugin</artifactId>    <version>2.5</version>    <executions>        <execution>            <phase>compile</phase> <!-- 与Maven编译生命周期绑定在一起 -->        </execution>    </executions></plugin>

maven-surefire-plugin(测试插件)

Maven2/3中用于执行测试的插件不是maven-test-plugin,而是maven-surefire-plugin,其实在大部分情况下,只要你的测试类遵循通用的命令约定(以Test结尾,以TestCase结尾、或者Test开头),就几乎不用知晓该插件的存在。但是当你想要跳过测试、排除某些测试类、或者使用一些TestNG特性的时候,就要用到了maven-surefire-plugin的一些配置选项了,配置如下所示:

<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-surefire-plugin</artifactId>    <version>2.14</version>    <configuration>        <skipTests>true</skipTests> <!-- 跳过测试 -->    </configuration>

maven-clean-plugin(清除插件)

主要作用就是清理构建目录下的全部内容,有些项目,构建时需要清理构建目录以外的文件,比如指定的库文件,这时候就需要配置来实现了,配置如下所示:

<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-clean-plugin</artifactId>    <version>3.0.0</version>    <configuration>        <!--<skip>true</skip>-->        <!--<failOnError>false</failOnError>-->        <!--当配置true时,只清理filesets里的文件,构建目录中得文件不被清理.默认是flase.-->        <excludeDefaultDirectories>false</excludeDefaultDirectories>        <filesets>            <fileset>                <!--要清理的目录位置-->                <directory>${basedir}/logs</directory>                <!--是否跟随符号链接 (symbolic links)-->                <followSymlinks>false</followSymlinks>             </fileset>        </filesets>    </configuration></plugin>

maven-war-plugin(打包插件)

主要作用就是用来打包的,在打包的时候经常需要排除一些文件,就需要对warSourceExcludes进行配置了,配置如下所示:

<plugin>    <artifactId>maven-war-plugin</artifactId>    <version>2.0.2</version>    <configuration>        <warSourceExcludes>WEB-INF/lib/**</warSourceExcludes>    </configuration></plugin>

总结

Maven是一个项目管理和自动化构建工具,项目遵循约定优于配置,这也是maven项目的一大特色。另外,maven本质上是一个插件框架,它的核心不执行任何具体的构建工作,全部都交给插件去执行,maven插件是与maven生命周期绑定在一起的。理解这些重要的核心点,对于maven的使用会有很大的帮助。

参考文章

Maven入门介绍

http://www.oracle.com/technetwork/cn/community/java/apache-maven-getting-started-1-406235-zhs.html

http://www.oracle.com/technetwork/cn/community/java/apache-maven-getting-started-2-405568-zhs.html

https://maven.apache.org/what-is-maven.html

Maven常用插件

http://www.infoq.com/cn/news/2011/04/xxb-maven-7-plugin

http://www.infoq.com/cn/news/2011/05/xxb-maven-8-plugin

欢迎关注国士梅花

国士梅花

原创粉丝点击