Maven

Maven

Maven含义

“Maven” 是 Apache 下的一个纯 Java 开发的开源项目。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤、依赖管理。Maven 的本质是一个项目管理工具,开发人员只需做一些简单的配置,就可以批量完成项目的构建、报告和文档的生成工作。Maven 是跨平台的,这意味着无论是在 Windows 上,还是在 Linux 或者 Mac 上,都可以使用同样的命令。

maven目录

1)bin

该目录包含了 mvn 运行的脚本,这些脚本用来配置 Java 命令,准备好 classpath 和相关的 Java 系统属性,然后执行 Java 命令。

其中 mvn 是基于 UNIX 平台的 shell 脚本,mvn.bat 是基于 Windows 平台的 bat 脚本。在命令行输入任何一条 mvn 命令时,实际上就是在调用这些脚本。

该目录还包含了 mvnDebug 和 mvnDebug.bat 两个文件,同样,前者是 UNIX 平台的 shell 脚本,后者是 Windows 平台的 bat 脚本。

2)boot

该目录只包含一个文件,以 maven 3.6.1 为例,该文件为 plexus-classworlds-2.6.0.jar。

plexus-classworlds 是一个类加载器框架,相对于默认的 java 类加载器,它提供了更丰富的语法以方便配置,Maven 使用该框架加载自己的类库。

3)conf

该目录包含了一个非常重要的文件 settings.xml。直接修改该文件,就能在机器上全局地定制 Maven 的行为。

一般情况下,我们更偏向于复制该文件至 ~/.m2/ 目录下(~表示用户目录),然后修改该文件,在用户范围定制 Maven 的行为。后面将会多次提到 settings.xml,并逐步分析其中的各个元素。

4)lib

该目录包含了所有 Maven 运行时需要的 Java 类库,Maven 本身是分模块开发的,因此用户能看到诸如 maven-core-3.0.jar、maven-model-3.0.jar 之类的文件。

此外,这里还包含一些 Maven 用到的第三方依赖,如 common-cli-1.2.jar、commons-lang-2.6.jar 等。

对于 Maven 2 来说,该目录只包含一个如 maven-2.2.1-uber.jar 的文件,原本各为独立 JAR 文件的 Maven 模块和第三方类库都被拆解后重新合并到了这个 JAR 文件中。可以说,lib 目录就是真正的 Maven。关于该文件,还有一点值得一提的是,用户可以在这个目录中找到 Maven 内置的超级 POM

5)LICENSE.txt

记录了 Maven 使用的软件许可证Apache License Version 2.0。

6)NOTICE.txt

记录了 Maven 包含的第三方软件。

7)README.txt

包含了 Maven 的简要介绍,包括安装需求及如何安装的简要指令等。

生命周期

Maven 拥有三套独立的生命周期,它们分别是 clean、default 和 site。clean 生命周期的目的是清理项目;default 生命周期的目的是构建项目;site 生命周期的目的是建立项目站点。

每个生命周期又包含了多个阶段。这些阶段在执行的时候是有固定顺序的。后面的阶段一定要等前面的阶段执行完成后才能被执行。

比如 clean 生命周期,它就包含 pre-clean、clean 和 post-clean 三个阶段。用户调用 pre-clean 时,只有 pre-clean 阶段被执行;调用 clean 时,先执行 pre-clean,再执行 clean 阶段;同理,当调用 post-clean 时,Maven 自动先执行 pre-clean、再执行 clean,最后执行 post-clean。

1. clean 生命周期

clean 生命周期的目的是清理项目,它包括以下三个阶段。

  • pre-clean:执行清理前需要完成的工作。
  • clean:清理上一次构建过程中生成的文件,比如编译后的 class 文件等。
  • post-clean:执行清理后需要完成的工作。
2. default 生命周期

default 生命周期定义了构建项目时所需要的执行步骤,它是所有生命周期中最核心部分,包含的阶段如下表所述,比较常用的阶段用粗体标记。

默认生命周期:校验-初始化-编译-测试-打包-集成测试-安装-部署

validate-initialize-compile-test-package-integrationTest-install-deploy

3. site 生命周期

site 生命周期的目的是建立和发布项目站点。Maven 可以基于 pom 所描述的信息自动生成项目的站点,同时还可以根据需要生成相关的报告文档集成在站点中,方便团队交流和发布项目信息。site 生命周期包括如下阶段。

  • pre-site:执行生成站点前的准备工作。
  • site:生成站点文档。
  • post-site:执行生成站点后需要收尾的工作。
  • site-deploy:将生成的站点发布到服务器上。

调用生命周期阶段

前面介绍了每套生命周期的各个阶段,那怎样通知 Maven 执行生命周期的哪个阶段呢?

有两种方式可以同 Maven 进行交互,一种是用 mvn 命令;另一种是在 M2Eclipse 中,使用对应的 Run As 菜单命令。

1. mvn 命令行指定执行周期阶段

这种方式都是在 CMD 命令行窗口中执行的,前提条件是要配置好安装的 Maven 环境变量(Path),并且将当前目录切换到 Maven 工程目录下。后面每个命令的例子都是基于 MvnSSMDemo.Service.Impl 工程进行的,它的当前目录是 E:\temp\demoMaven\MvnSSMDemo.Service.Impl。

1)mvn clean:调用 clean 生命周期的 clean 阶段,实际执行的是 clean 生命周期中的 pre-clean 和 clean 阶段,如图 所示。

mvn clean提示

2)mvn test:该命令调用 default 生命周期中的 test 阶段。实际执行的阶段包括 validate、initialize、generate-sources…compile…test-compile、process-test-classes、test,也就是把 default 生命周期中从开始到 test 的所有阶段都执行完了,而且是按顺序执行。最后运行效果如图所示。

mvn test提示

3)mvn clean install:该命令调用 clean 生命周期的 clean 阶段和 default 生命周期的 install 阶段。

实际执行的是 clean 生命周期中的 pre-clean、clean 两个阶段和 default 生命周期中从开始的 validate 到 install 的所有阶段。

该命令结合了两个生命周期。在实际项目构建中,每执行一个行的构建,先清理以前构建的旧文件是一个好习惯。最后运行效果如图 3 所示。

mvn clean install提示

4)mvn clean deploy site-deploy:该命令调用 clean 生命周期中的 pre-clean、clean 阶段,default 生命周期中从 validate 到 deploy 的所有阶段,以及 site 生命周期中的 pre-site、site、post-site 和 site-deploy 阶段。

最后的结果是把该项目编译、测试、打包好发布到远程仓库,同时还将生成好的站点发布到站点服务器。

Maven坐标

在 Maven 仓库中,是用坐标标记来一一对应地管理每个构件的。一个完整的坐标信息,由 groupId、artifactId、version、packaging、classifier 组成,如下是一个简单的坐标定义。

<groupId>org.SpringFramework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.7.RELEASE</version>
<packaging>jar</packaging>v
1. groupId

定义当前 Maven 项目从属的实际项目。关于 groupId 的理解如下所示。

1)Maven 项目和实际项目不一定是一一对应的。比如 SpringFramework,它对应的 Maven 项目就有很多,如 spring-core、spring-context、spring-security 等。造成这样的原因是模块的概念,所以一个实际项目经常会被划分成很多模块。

2)groupId 不应该同开发项目的公司或组织对应。原因比较好理解,一个公司和一个组织会开发很多实际项目,如果用 groupId 对应公司和组织,那 artifactId 就只能是对应于每个实际项目了,而再往下的模块就没法描述了,而往往项目中的每个模块是以单独的形式形成构件,以便其他项目重复聚合使用。

3)groupId 的表述形式同 Java 包名的表述方式类似,通常与域名反向一一对应。

2. artifactId

定义实际项目中的一个 Maven 项目(实际项目中的一个模块)。

推荐命名的方式为:实际项目名称-模块名称。

比如,org.springframework 是实际项目名称,而现在用的是其中的核心模块,它的 artifactId 为 spring-core。

3. version

定义 Maven 当前所处的版本。如上的描述,用的是 4.2.7.RELEASE 版本。需要注意的是,Maven 中对版本号的定义是有一套规范的。

4. packaging

定义 Maven 项目的打包方式。

打包方式通常与所生成的构件文件的扩展名对应,比如,.jar、.ear、.war、.pom 等。另外,打包方式是与工程构建的生命周期对应的。比如,jar 打包与 war 打包使用的命令是不相同的。最后需要注意的是,可以不指定 packaging,这时候 Maven 会自动默认成 jar。

5. classifier

定义构件输出的附属构件。

附属构件同主构件是一一对应的,比如上面的 spring-core-4.2.7.RELEASE.jar 是 spring-core Maven spring-core 项目的主构。

Maven spring-core 项目除了可以生成上面的主构件外,也可以生成 spring-core-4.2.7.RELEASE-javadoc.java 和 spring-core-4.2.7.RELEASE-sources.jar 这样的附属构件。这时候,javadoc 和 sources 就是这两个附属构件的 classifier。这样就为主构件的每个附属构件也定义了一个唯一的坐标。

最后需要特别注意的是,不能直接定义一个 Maven 项目的 classifier,因为附属构件不是由 Maven 项目构建的时候直接默认生成的,而是由附加的其他插件生成的。

前面介绍的组成坐标的 5 个要素中,groupId、artifactId 和 version 是必需的,packaging 是可选的,默认是 jar,而 classifier 是不能直接定义的。同时,Maven 项目的构件文件名与坐标也是有对应关系的,一般规则是 artifactId-version[-classifier].packaging。

Maven仓库

1)定义

在 Maven 中,所有的依赖、插件以及 Maven 项目构建完的输出都是以构件的形式存在的,都叫构件。任何一个构件都是由一组坐标信息唯一标识的。
在一台用于项目开发的计算机中有可能存在很多 Maven 项目,比如前面介绍的那么多样例代码,它们都是分布在不同的 Maven 项目中的。这些 Maven 项目肯定都会用到 compiler 插件,除了这个插件外,还有很多特有的构件。比如,MvnSSHDemo.Struts 中就用到了 struts2 构件,MvnSSH.Spring 中就用到了 Spring 相关的构件,MvnSSM.SpringMVC 中用到了 Spring-web 构件等。而且这些直接依赖中,又会引入很多间接依赖,中间也肯定有交叉的构件依赖在引用。

如果同以前的开发模式一样,将各自用到的依赖对应的构件文件都体现到自己的对应目录,比如 lib 目录下的话,就会发现同样的文件会在很多工程里面重复存在。这样不仅造成了大量的磁盘空间浪费,也不便于统一管理。

所以Maven 统一存储了所有 Maven 项目用到的构件,这些构件都是共享的。当某个 Maven 项目要使用某些构件的时候,就直接通过构件的坐标引用共享的构件,不需要复制到每个 Maven 工程的独立物理目录中去。这个统一的位置就叫***仓库***

Maven 的仓库,实际上就是 Maven 构件的公共仓库。在实际的 Maven 项目中,只需指明这些依赖的坐标,需要的时候(编译、测试、运行),由 Maven 自动根据坐标找到对应的构件后使用。为了完全实现重用,Maven 项目构建完毕后生成的构件也可以安装或部署到仓库中,供其他 Maven 项目使用。

Maven配置远程仓库

用户可以从中央仓库中找到绝大部分流行的构件,但是毕竟不能找到所有构件。对那些在中央仓库中没有的构件,又要怎么办呢?可以在 pom.xml 中添加另外一个远程仓库。比如,将 jboss Maven 远程仓库添加到 Maven,需要在 Maven 工程的 pom.xml 中添加如下配置。

<project>
    ...
    <repositories>
        <repository>
            <id>jboss</id>
            <name>JBoss Maven Repository</name>
            <url>http://repository.jboss.com/maven2/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <layout>default</layout>
        </repository>
    </repositories>
    ...
</project>
快照(SNAPSHOT)版本介绍

Maven 为什么要添加一个快照版本的控制呢?

假设张三在开发用户管理模块的 1.1 版本,该版本还没有正式发布。以前的用户管理模块和权限管理模块是由李四在单独开发的。其中,权限管理模块的功能是依赖用户管理模块的。

在开发过程中,张三经常要将最新的用户管理模块构建输出,交给李四,让他对权限管理模块进行开发集成和调试。这种问题,如果由用户自己手动控制的话,相对比较麻烦。但 Maven 基于快照机制,就能自动解决这个问题。

基于 Maven 的快照机制,张三只需将用户管理模块的版本设置成 1.1-SNAPSHOT,然后发布到私服中。

在发布过程中,Maven 会自动为构件打上时间戳,比如 1.1-20161211.111111-11,表示 2016 年 12 月 11 日 11 点 11 分 11 秒的第 11 次的快照。有了这个时间戳,Maven 就能随时找到仓库中用户管理构件 1.1-SNAPSHOT 版本的最新文件。

这时,李四配置对用户管理模块的 1.1-SNAPSHOT 版本的依赖,当他构建权限管理模块的时候,Maven 会自动从仓库中检测用户管理 1.1-SNAPSHOT 的最新构件,发现最新构件后就自动下载。

Maven 默认情况下,每天检测一次(具体实际情况,由参考配置的 updatePolicy 控制),当然,也可以使用 mvn-U 强制让 Maven 检测更新。如 mvn clean install-U。

基于这样的机制,张三在构建成功后,将构件发布到仓库,李四可以完全考虑用户管理模块的构件,并且他还能确保随时得到用户管理模块的最新可用的快照构件,这些所有的一切都由 Maven 自动完成。

快照版本只应该在开发团队内部的项目或模块之间依赖使用。这个时候,团队成员对这些快照版本的依赖具有完全的理解和控制权利。

Maven依赖配置和依赖范围

依赖一般分以下两个层次理解:

1)在 Maven 项目的 pom.xml 中配置所需要构件的坐标,也就是配置依赖。还有就是 Maven 在构建项目的时候,根据坐标从仓库中找到坐标所对应的构件文件,并且把它们引入 Maven 项目中来,也就是 Maven 引用。

2)由 Maven 构建的时候自己搞定。前面也介绍了 Maven 基于坐标寻找要执行的插件的思路。实际上,插件本身就是一个特殊的构件。查找插件的思路也就是依赖查找的思路。这里需要把握的更多的是第一层次,即怎样配置依赖,以及指定依赖内部的关系和优化等。

依赖的配置

掌握依赖,从配置开始。接下来介绍一下依赖的配置。依赖是配置在 pom.xml 中的,如下是关于依赖配置的大概内容:

<project>
    ...
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>
                ...
            </artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...</optional>
            <exclusions>
                <exclusion>...</exclusion>
            </exclusions>
        </dependency>
        ...
    </dependencies>
    ...
</project>
这些元素的作用
  • groupId、artifactId 和 version:依赖的基本坐标。对于任何依赖,基本坐标是最基本、最重要的,因为 Maven 是根据坐标找依赖的。
  • type:依赖的类型,同项目中的 packaging 对应。大部分情况不需要声明,默认是 jar。
  • scope:依赖的范围,详细情况后面介绍。
  • optional:标记依赖是否可选,详细情况后面介绍。
  • exclusions:排除传递性依赖,详细情况后面介绍。
依赖的范围

Java 中有个环境变量叫 classpath。JVM 运行代码的时候,需要基于 classpath 查找需要的类文件,才能加载到内存执行。

Maven 在编译项目主代码的时候,使用的是一套 classpath,主代码编译时需要的依赖就添加到这个 classpath 中去;Maven 在编译和执行测试代码的时候,又会使用一套 classpath,这个动作需要的依赖就添加到这个 classpath 中去;Maven 项目具体运行的时候,又有一个独立的 classpath,同样运行时需要的依赖,肯定也要加到这个 classpath 中。这些 classpath,就是依赖的范围。

依赖的范围,就是用来控制这三种 classpath 的关系(编译 classpath、测试 classpath 和运行 classpath),接下来分别介绍依赖的范围的名称和意义。

1)compile

编译依赖范围。如果在配置的时候没有指定,就默认使用这个范围。使用该范围的依赖,对编译、测试、运行三种 classpath 都有效。

2)test

测试依赖范围。使用该范围的依赖只对测试 classpath 有效,在编译主代码或运行项目的时候,这种依赖是无效的。

3)provided

已提供依赖范围。使用此范围的依赖,只在编译和测试 classpath 的时候有效,运行项目的时候是无效的。比如 Web 应用中的 servlet-api,编译和测试的时候就需要该依赖,运行的时候,因为容器中自带了 servlet-api,就没必要使用了。如果使用了,反而有可能出现版本不一致的冲突。

4)runtime

运行时依赖范围。使用该范围的依赖,只对测试和运行的 classpath 有效,但在编译主代码时是无效的。比如 JDBC 驱动实现类,就需要在运行测试和运行主代码时候使用,编译的时候,只需 JDBC 接口就行。

5)system

系统依赖范围。该范围与 classpath 的关系,同 provided 一样。但是,使用 system 访问时,必须通过 systemPath 元素指定依赖文件的路径。因为该依赖不是通过 Maven 仓库解析的,建议谨慎使用。

如下代码是一个使用 system 范围的案例。

<dependency>
    <groupId>xxx</groupId>
    <artifactId>xxx</artifactId>
    <version>xx</version>
    <scope>system</scope>
    <systemPath>e:/xxxx/xxx/xx.jar</systemPath>
</dependency>
6)import

导入依赖范围。该依赖范围不会对三种 classpath 产生实际的影响。它的作用是将其他模块定义好的 dependencyManagement 导入当前 Maven 项目 pom 的 dependencyManagement 中。比如有个 SpringPOM Maven 工程,它的 pom 中的 dependencyManagement 配置如下:

<project>
    ...
    <groupId>cn.com.mvn.pom</groupId>
    <artifactId>SpringPOM</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    ...
    <dependencyManagement>
        <dependencies>
            <!-- spring -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${project.build.spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${project.build.spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${project.build.spring.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    ...
</project>

接下来创建一个新的 Maven 工程 Second,要将 First 工程中 pom 中定义的 dependency-Management 原样合并过来,除了复制、继承之外,还可以编写如下代码,将它们导入进去。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>cn.com.mvn.pom</groupId>
            <artifactId>SpringPOM</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

传递性依赖

在使用 Maven 之前,如果要基于 Spring 框架开发项目,除了要加入 Spring 框架的 jar 包外,还需要将 Spring 框架所用到的第三方 jar 包加入。否则编译通过,但是运行的时候就会出现 classNotFound 异常。

为了解决这种问题,一般有两种方式:一种是下载 Spring 的 dependencies.zip 包,将其中的所有 jar 包都导入工程;另一种是根据运行时的报错信息,确定哪些类没有,再将包含这些类的 jar 包下载下来导入。

第一种方式虽然可以一次性解决所有需要 jar 包的导入问题,但是当查看工程的 jar 包会发现,有不少多余的 jar 包。这些多余的 jar 包不仅仅加大了项目的体积,还有可能同其他框架所导入的 jar 包有版本冲突。

第二种方式虽然不会有多余的 jar 包存在,但是要根据每次启动的错误,一个个找到 jar 包,再导入。想象如果有 10 个 jar 包,就要启动 10 次,查看 10 次错误分别导入,有多麻烦。

Maven 的传递依赖机制就能解决这样的问题。

当项目基于 Spring 框架实现的时候,只需将 Spring 的依赖配置到 pom 的依赖元素就行。至于 Spring 框架所依赖的第三方 jar 包,用户不用处理,Maven 自己通过检测 Spring 框架的依赖信息将它们导入项目中来。而且只会导入 Spring 框架所需要的,不会导入多余的依赖。

也就是说,Maven 会解析项目中的每个直接依赖的 pom,将那些必要的间接依赖以传递依赖的形式引入项目中。

当然,传递依赖在将间接依赖引入项目的过程中也有它自己的规则和范围。这个规则和范围是同前面介绍的依赖范围紧密关联的。

现在有三个项目(A、B 和 C 项目),假设 A 依赖 B,B 依赖 C,这样把 A 对 B 的依赖叫第一直接依赖,B 对 C 的依赖叫第二直接依赖,而 A 对 C 的依赖叫传递依赖(通过 B 传递的)。

中间 A 到 B 第一直接依赖的范围和 B 到 C 第二直接依赖的范围,就共同决定了 A 到 C 的传递依赖范围。它们的影响效果,就如表 1 所示。

坐标第一列表示第一直接依赖的范围,第一行表示第二直接依赖的范围,中间的交叉点为共同影响后的传递依赖的范围。

依赖compiletestprovidedruntime
Compilecompileruntime
testtesttest
providedprovidedprovidedprovided
runtimeruntimeruntime


通过前面的表格,可以得出如下规律。

  • 当第二直接依赖为 compile 的时候,传递依赖同第一直接依赖一致。
  • 当第二直接依赖为 test 的时候,没有传递依赖。
  • 当第二直接依赖为 provided 的时候,值将第一直接依赖中的 provided 以 provided 的形式传递。
  • 当第二直接依赖为 runtime 的时候,传递依赖的范围基本上同第一直接依赖的范围一样,但 compile 除外,compile 的传递依赖范围为 runtime。

依赖的调解

在使用 Maven 自动提供的传递依赖后,可以解决对应的依赖管理,特别是间接依赖管理中遇到的问题。但是,当多个直接依赖都带来了同一个间接依赖,而且是不同版本的间接依赖时,就会引起重复依赖,甚至包冲突的问题。

那么,Maven 在传递依赖的时候是按什么规则来的呢?

1. 依赖调解原则

Maven 依赖调解原则有两个:一个是路径优先原则;另一个是声明优先原则。当路径优先原则搞不定的时候,再使用声明优先原则。

比如有个项目 A,它有两个依赖:A→B→C→T(1.0),A→D→T(2.0)。会发现,A 最终对 T(1.0)和 T(2.0)都有间接依赖。这时候 Maven 会自动判断它的路径,发现 T(2.0)的路径长度为 2,T(1.0)的路径长度为 3,以最短路径为原则,将 T(2.0)引入当前项目 A。

如果有个项目 A,它有两个依赖:A→B→T(1.0),A→C→T(2.0)。这时候两条路径都是一样的长度 2,那 Maven 到底把哪个引入项目 A 呢?这时候 Maven 会判断哪个依赖在 pom.xml 中先声明,选择引入先声明的依赖。

2.可选依赖

在实际项目中,存在一些比较特殊的依赖。比如数据访问层模块对数据库驱动的依赖就比较特殊了。DAO 层要访问数据库的时候,需要加入数据库驱动依赖,而且不同数据库驱动依赖是不一样的。如果在设计 DAO 层的时候,是按跨数据库标准实现的,这就引出了一个新问题,是在 pom.xml 中配置 MySQL 驱动依赖呢?还是配置 Oracle 驱动依赖?或者两个都配置?

其实仔细想想,前面三种选项都不合适。单独配置 MySQL 或 Oracle,这样就不能跨数据库了。两个数据库都配置,驱动之间就会有冲突,或有多余的依赖。

这时候,就直接把这两个数据库驱动的依赖都设置成可选依赖,代码如下:

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.34</version>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>oracle</groupId>
        <artifactId>ojdbc14</artifactId>
        <version>10.2.0.4</version>
        <optional>true</optional>
    </dependency>
</dependencies>

在应用项目中再具体指定使用哪个依赖,例如:

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.34</version>
    </dependency>
</dependencies>

需要说明的是,在实际项目中建议不要使用可选依赖。虽然可选依赖满足了对一个模块的特征多样性,同时还提供了更多的选择,但是在实际配置中,好像不仅没有减少配置代码,还增多了重复复制的机会。

同时从面向对象分析和设计的思路来说,也是建议遵循单一职责原则,也就是一个类只有一个功能,不要糅合太多的功能,这样不方便理解、开发和维护。

所以实际项目中,一般对不同数据库的驱动单独创建一个 Maven 工程。其他项目需要基于哪个数据库进行操作的话,引用对应的 Maven 的工程以来就行,用传递依赖引入需要的数据库驱动依赖。

创建Maven项目(cmd命令)

Maven 项目同 MyEclipse 或其他工具产生的项目一样,有自己的目录结构和特殊的意义。

比如一般有如下目录。

  • src\main\java,用来存放项目的 Java 源代码。
  • src\main\resources,用来存放项目相关的资源文件(比如配置文件)。
  • src\test\java,用来存放项目的测试 Java 源代码。
  • src\test\resource,用来存放运行测试代码时所依赖的资源文件。

当然,还有一个 pom.xml 文件,该文件配置 Maven 管理的所有内容。

这里可以按 Maven 的要求,自动创建目录结构,按 Maven 的要求添加项目相关的配置文件,这样确实可以实现,但是很烦琐。已经有人用代码将这些要做的事情全都封装实现了,如同在 MyEclipse 中创建工程的那种图形化导向页面一样(这种效果到使用 MyEclipse+Maven 的时候体现),只要按它的步骤输入信息和命令,完成后自动产生项目架构。

这里简单介绍一下有关的命令和信息。

1)命令。命令很简单,就是创建项目的命令 create。

人们把要调用哪个软件的 create 命令创建项目叫插件(plugin)。创建项目的插件叫 Archetype 插件(archetype-plugin)。

2)信息。和项目相关的信息包括 groupId(组 Id)、artifactId(构件 Id)、packageName(包名)、version(版本)。

其实 packageName 和 version 好理解。程序员写的类,肯定要放在一个标准包下或标准包的子包下,packageName 指标准包;version 是当前代码的版本号。

这里的 groupId 和 artifactId 同部门名称和组名称一样,用来唯一确定一个项目(软件、功能)。有些地方会把这两个描述的信息合起来叫“坐标”。

用命令产生项目的方式有两种。

使用命令向导一步步创建项目

1)在硬盘上创建一个空的目录,用来存放Maven项目,如E:\temp\demoMaven。

2)打开 CMD 窗口,用 cd 命令,切换到 demoMaven 目录,如图所示

Maven项目目录(1)

3)在 CMD 窗口中输入“mvn archetype:generate”,按 Enter 键。

联网初始化一段时间后(一般不少于 5 分钟),会一步步提示输入 groupId、artifactId、version、packageName 等信息。最后创建成功,而且可以在 E:\temp\demoMaven 空目录下发现一个同 artifactId 一样的目录,这就是创建的项目目录。

在命令中输入所有必要信息直接创建项目

1)在硬盘上创建一个空的目录,用来存放 Maven 项目,如 E:\temp\demoMaven。

2)打开 CMD 窗口,用 cd 命令,切换到 demoMaven 目录,如图 1 所示。

3)在 CMD 窗口中输入如下命令并按 Enter 键。

mvn org.apache.maven.plugins:maven-archetype-plugin:2.2:creat
-DgroupId=com.mengma.demo
-DartifactId=HelloWorld
-DpackageName=com.mengma.demo

注:

  • org.apache.maven.plugins:maven-archetype-plugin:2.2,指使用 groupId 为 org.apache.maven.plugins,artifactId 为 maven-archetype-plugin,版本为 2.2 的 Archetype插件。
  • -DgroupId=cn.com.mvnbook.demo,指定要创建的工程的 groupId。
  • -DartifactId=MVNBookTP01,指定工程的 artifactId。
  • -DpackageName=cn.com.mvnbook.demo.tp01,指定工程代码的标准包。

Maven 执行命令的时候,会在本地寻找是否有指定版本的 Archetype 插件,如果没有,就需要联网下载。最后显示的正常状态如图所示。

Maven创建项目提示

同时,它会在 demoMaven 目录下创建一个新的 HelloWorld 目录。到这里,就可以使用 Archetype 插件创建第一个工程的架构了。

添加样例代码

为了完成体验,需要写两个代码:一个是 HelloWorld.java,放在 src\main\java 目录下;另一个是 TestHelloWorld.java,用来体现测试,放在 src\test\java 目录下。下面介绍它们的内容。

HelloWorld.java 代码如下所示:

package com.mengma.demo.mvn;
/**
* 这是为了研究Maven,写的第一个Java代码 功能很简单,输出一个HelloWorld的问候
*
* @ author Noble
* @ version 1.0
*/
public class HelloWorld {
    /**
     * 输出问候
     * @ param name String,说话人名称
     * @ return String 格式是:xxx say HelloWorld
     **/
    public String say(String name) {
        return name + " say HelloWorld";
    }
}

TestHelloWorld.java 代码如下所示:

package com.mengma.demo.mvn;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TestHelloWorld {
    private HelloWorld hello;
    @Before
    public void init() {
        hello = new HelloWorld();
    }
    @Test
    public void testSay() {
        String name = "张三";
        String exp = "张三" + "say HelloWorld";
        String act = hello.say(name);
        Assert.assertEquals(exp, act);
    }
    @After
    public void destory() {
        hello = null;
    }
}
编写 Maven 骨架文件

代码写好了,接下来要通过配置文件让 Maven 管理。这时要用到 pom.xml,即骨架文件。该文件在创建工程时会在工程目录下自动生成 pom.xml。

<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.mengma.demo</groupId>
    <artifactId>HelloWorld</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>HelloWorld</name>
    <url>http://maven.apache.org</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

注:

  • 创建工程时指定的 groupId。
  • 创建工程时指定的 artifactId。
  • 当前工程的版本。
  • 工程编译好后,打成 jar 包安装发布。
  • 测试时需要依赖的 JUnit 的 groupId。
  • 测试时需要依赖的 JUnit 的 artifactId。
  • 测试时需要依赖的 JUnit 的版本。
  • 指定测试依赖的作用范围是测试。

编译和测试

打开 CMD 窗口,操作步骤如下所示:

  1. 将目录切换到工程目录下(HelloWorld)。

  2. 输入“mvn clean”,按 Enter 键清空以前编译安装过的历史结果。

  3. 输入“mvn compile”,按 Enter 键编译源代码。

  4. 输入“mvn test”,按 Enter 键运行测试案例进行测试。

  5. 输入“mvn install”,按 Enter 键,将当前代码打成 jar 包,安装到 Maven 的本地管理目录下,其他 Maven 工程只要指定坐标就可以使用。

    junit
    4.7
    test



注:

- 创建工程时指定的 groupId。
- 创建工程时指定的 artifactId。
- 当前工程的版本。
- 工程编译好后,打成 jar 包安装发布。
- 测试时需要依赖的 JUnit 的 groupId。
- 测试时需要依赖的 JUnit 的 artifactId。
- 测试时需要依赖的 JUnit 的版本。
- 指定测试依赖的作用范围是测试。

## 编译和测试

打开 CMD 窗口,操作步骤如下所示:

1. 将目录切换到工程目录下(HelloWorld)。
2. 输入“mvn clean”,按 Enter 键清空以前编译安装过的历史结果。
3. 输入“mvn compile”,按 Enter 键编译源代码。
4. 输入“mvn test”,按 Enter 键运行测试案例进行测试。
5. 输入“mvn install”,按 Enter 键,将当前代码打成 jar 包,安装到 Maven 的本地管理目录下,其他 Maven 工程只要指定坐标就可以使用。


到现在为止,编码的操作就完成了,包括工程的创建、源代码的编写、单元测试代码的编写、代码的编译、测试案例的运行以及最后的打包。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值