# Maven

Maven是一个口语化的词,代表专家内行。Apache Maven项目是一个项目管理工具,主要服务于Java平台的项目构建,依赖管理和项目信息管理,它包含了:

  • 项目对象模型(POM:Project Object Model)
  • 项目生命周期(Project Lifecycle)
  • 依赖管理系统(Dependency Management System)
  • 中间件仓库,根据坐标即可找到

我们一直在不停地寻找避免重复的方法,设计的重复,编码的重复,文档的重复,构建的重复。Maven最大化地消除了构建的重复,抽象了构建生命周期,为绝大部分的构建任务提供了已实现的插件,将项目过程规范化,自动化,高效化,并具有扩展性。

使用Maven不用再在本地拷贝jar包,在Maven管理配置中配置好所需构件的坐标,Maven会去本地仓库查找,如果没有会去中央仓库下载。

# Maven安装

  • 从官方下载安装包,解压到本地,将该路径加入到Path中。一般设置Maven的环境变量为maven_home。
  • 配置本地仓库位置,在conf/settings.xml中配置<localRepository>/path/to/local/repo</localRepository>

# 安装目录

Maven安装目录结构如下:

  • bin:mvn运行脚本,其中m2.conf是classworlds配置文件。
  • boot:该目录只包含一个文件,它是一个类加载器框架,相对于默认的Java类加载器,它的语法更丰富,Maven使用该加载器来加载自己类库。
  • conf:settings.xml文件能全局定制Maven行为。
  • lib:包含Maven运行时需要的Java类库。

# 项目目录

Maven管理的项目目录结构如下:

目录 说明
src/main/java 核心代码部分
src/main/resources 资源文件部分
src/main/webapp 页面资源,js,css,图片
src/test/java 测试代码部分
src/test/resources 测试配置文件部分
pom.xml pom文件

# 系统安装

  1. 设置环境变量M2_HOME到Maven目录结构。
  2. 然后在path中添加%M2_HOME%\bin。

# IntelliJ集成

  1. 在configure->settings->Build,Execution,Deployment->BuildTools->Maven处设置。
  2. 如果希望使用离线本地仓库,在不联网的时候也能使用,可在Runner中的VM Options输入:-DarchetypeCatalog=internal。
  3. 使用骨架创建工程:
    1. Java项目:maven-archetype-quikstart。
    2. web项目:maven-archetype-webapp。
  4. 添加资源目录:在Project Structure->Modules->Web Resource Directories。

# Eclipse集成

问题1:在Eclipse中更改Maven

  1. 在Windows->Preferences->Maven->Installations中添加新的maven工具并更改。

问题2:Eclipse中的maven,无法自动下载相应的jar包

  1. 在Windows->Preferences->Maven处取消勾选Do not automatically update dependencies from remote repositories.
  2. 如果上面操作后还不下载,需要清除下本地缓存,在Run->run configurations –>Maven Build –>选择报错的项目,在Goals处填写clean install -U,然后右键->Maven->Update Project即可。

# Maven启动tomcat

maven可以使用tomcat的插件然后将项目运行在服务器中,在pom.xml中设置:

<build>
  <!--maven插件-->
  <plugins>
      <!--jdk编译插件-->
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
              <source>1.8</source>
              <target>1.8</target>
              <encoding>utf-8</encoding>
          </configuration>
      </plugin>
      <!--tomcat插件-->
      <plugin>
          <groupId>org.apache.tomcat.maven</groupId>
          <!-- tomcat7的插件, 不同tomcat版本这个也不一样 -->
          <artifactId>tomcat7-maven-plugin</artifactId>
          <version>2.1</version>
          <configuration>
              <!-- 通过maven tomcat7:run运行项目时,访问项目的端口号 -->
              <port>80</port>
              <!-- 项目访问路径  本例:localhost:9090,  如果配置的aa, 则访问路径为localhost:9090/aa-->
              <path>/travel</path>
          </configuration>
      </plugin>
  </plugins>
</build>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

使用maven命令运行:

mvn tomcat7:run

# 设置代理

在Maven安装目录下conf->setting.xml文件中设置:

<proxies>
 <!-- proxy
  | Specification for one proxy, to be used in connecting to the network.
  |-->
 <proxy>
   <id>quickq</id>
   <active>true</active>
   <protocol>http</protocol>
   <!-- <username>proxyuser</username>
   <password>proxypass</password> -->
   <host>127.0.0.1</host>
   <port>11000</port>
   <nonProxyHosts>localhost</nonProxyHosts>
 </proxy>
</proxies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Maven设置镜像源

Maven也可以设置其他镜像源,比如阿里云Maven (opens new window)。在conf->setting.xml文件中设置:

<mirror>
  <id>aliyunmaven</id>
  <mirrorOf>*</mirrorOf>
  <name>阿里云公共仓库</name>
  <url>https://maven.aliyun.com/repository/public</url>
</mirror>
1
2
3
4
5
6

如果查找Maven包的坐标可以去阿里云云效Maven (opens new window)

# 基本操作

Maven中的基本操作对应相应的生命周期阶段,一个规范化的构建流程有:

  • 清理
  • 编译
  • 测试
  • 报告
  • 打包
  • 部署

它们基本上分为:

  • clean生命周期:在进行真正的构建之前进行一些清理工作
    • pre-clean:执行一些需要在clean之前完成的工作
    • clean:移除所有上一次构建生成的文件
    • post-clean:执行一些需要在clean之后立刻完成的工作
  • default生命周期:构建的核心部分,编译、测试、打包、安装、部署等等
    • validate:验证工程是否正确,所有需要的资源是否可用。
    • compile:编译项目的源代码。
    • test:使用合适的单元测试框架来测试已编译的源代码。这些测试不需要已打包和布署。
    • package:把已编译的代码打包成可发布的格式,比如jar。
    • integration-test:如有需要,将包处理和发布到一个能够进行集成测试的环境。
    • verify:运行所有检查,验证包是否有效且达到质量标准。
    • install:把包安装到maven本地仓库,可以被其他工程作为依赖来使用。
    • deploy:在集成或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。
  • site生命周期:生成项目报告,站点,发布站点
    • pre-site:生成项目站点之前需要完成的工作
    • site:生成项目站点文档
    • post-site:生成项目站点之后需要完成的工作
    • site-deploy:将项目站点发布到服务器

常用的命令如下:

阶段 命令 描述
clean mvn clean 清除target目录
compile mvn compile 编译
test mvn test 编译src和test下代码
package mvn package 打包的格式可在pom.xml中packaging设置
install mvn install 打包,并安装到本地仓库
deploy mvn deploy 打包,并把包拷贝到远程仓库

# 仓库分类

Maven的仓库只分为两类:本地仓库和远程仓库。Maven根据坐标寻找构件的时候首先会查看本地仓库,如果没有会去远程仓库查找。有一种特殊远程仓库是中央仓库,Maven自带的仓库,当本地仓库没有时就会默认查找这个仓库,此外还可以自己搭建私服,来代理外部的远程仓库。

# 概念模型

Maven的概念模型如下:

conceptModel

Maven主要的功能是:

  1. 构件依赖管理
  2. 构件生命周期管理
  3. 项目构建管理

# POM文件

POM是Project Object Model(项目对象模型)的缩写,该文件中包含了依赖关系、构建目录、源目录、测试源目录、插件、目标等项目信息和配置信息。Maven读取该文件,然后执行构建目标。

# 基本元素

元素 含义
project pom文件根元素,表示一个项目
modelVersion project元素的子元素,指定modelVersion版本号,应该设置为4.0.0
groupId project元素的子元素,指定项目所属的group
artifactId project元素的子元素,项目交付件ID,项目交付件是指项目的最终交付文件,如jar、zip、war等
version project元素的子元素,项目(交付件)的版本号

其中groupId,artifactId,version构成了一个构件的坐标,来唯一标识一个构件。

# 常用元素

元素 含义
packaging 定义打包类型,如jar, war
name 指定项目名称
url 指定项目url
dependencies 表示项目依赖项列表
dependency 表示单个依赖项,这个元素是依赖项列表元素的子元素
scope 单个依赖项的作用域,作用域可以是compile、test、runtime、provided、system之一,作用域的意图是限定该依赖项作用范围,如test,则表明依赖项只在test时起作用

# 依赖管理

Maven 一个核心的特性就是依赖管理。当我们处理多模块的项目(包含成百上千个模块或者子项目),模块间的依赖关系就变得非常复杂,管理也变得很困难。Maven 通过读取项目文件(pom.xml),找出它们项目之间的依赖关系。我们需要做的只是在每个项目的 pom 中定义好直接的依赖关系。

# 依赖范围

添加坐标时需要指定依赖范围,它包括:

  • compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。典型的例子是spring-core,在编译、测试和运行的时候都需要使用该依赖。
  • test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子是JUnit,它只有在编译测试代码及运行测试的时候才需要。
  • provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试class-path有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于Tomcat等容器已经提供,就不需要Maven重复地引入一遍。
  • runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行class-path有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
  • system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建。

依赖范围由弱到强是:test->runtime->provided->compile。

总结就是:

依赖范围 对于编译classpath有效 对于测试classpath有效 对于运行时classpath有效 例子
compile Y Y Y spring-core
test N Y N Junit
provided Y Y N servlet-api,jsp-api
runtime N Y Y JDBC驱动
system Y Y N 本地的,Maven仓库之外的类库

# 示例

下面是一个在Intellij中的pom.xml,为了避免和Intellij中tomcat的类库冲突,scope设置为provided。

<?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.test</groupId>
  <artifactId>maven_web</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <!--放置的都是项目运行所依赖的jar包-->
  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8888</port>
        </configuration>
      </plugin>
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
            <target>1.8</target>
            <source>1.8</source>
            <encoding>UTF-8</encoding>
          </configuration>
        </plugin>
    </plugins>
  </build>

</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

因为是tomcat中也有servlet-api包,如果默认为compile,即项目在编译,测试,运行阶段都需要这个artifact对应的jar包在classpath中,那么在使用tomcat运行时就会发生了冲突,由于provided只影响编译和测试阶段,在编译测试阶段,我们需要这个artifact对应的jar包在classpath中,而在运行阶段,假定目标的容器(比如我们这里的tomcat容器)已经提供了这个jar包,所以无需我们这个artifact对应的jar包了,那么在实际发布时会默认使用第三方web服务器中提供的jar包,而不会使用本jar包。注意这些声明没有传递性,间接依赖不起作用。

# 实例

# mvn创建

使用命令创建一个项目:

mvn archetype:generate -DgroupId=com.xie.com -DartifactId=xie-maven-web -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

项目打包:

mvn package

# IDE创建

在Intellij或者Eclipse中生成Maven项目,可以在创建项目处选择Maven,然后根据提示进行即可。

# 常见问题

# 不支持发行版本5

在IDEA使用Maven构建项目提示总提示 Error:java: 错误: 不支持发行版本 5,是因为Intellij IDEA用Maven来构建项目,若pom.xml没有指定版本,总是默认Language level 5 与 Java Compiler 1.5。

解决办法就是修改pom.xml:

  1. 添加properties节点:

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--修改Language level-->
        <maven.compiler.source>1.8</maven.compiler.source>
        <!--修改Java Compiler-->
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    1
    2
    3
    4
    5
    6
    7
  2. pom.xml中添加build节点:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12