欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

maven快速入门第五讲——依赖的作用范围

程序员文章站 2022-03-26 21:37:36
...

引子

在前一讲中,使用maven里面带有的tomcat7插件运行maven项目成功后,在Google Chrome浏览器访问HelloServlet时,发现报如下错误。
maven快速入门第五讲——依赖的作用范围
为什么会报错呢?这是因为我们本地tomcat服务器的lib目录中已经存在servlet-api.jar和jsp-api.jar这俩jar包了。
maven快速入门第五讲——依赖的作用范围
而我们的maven项目又再次引入了这俩jar包。
maven快速入门第五讲——依赖的作用范围
这样,当把咱们的maven项目部署到本地tomcat服务器上去时,就会存在这俩jar包冲突的问题,所以才会报错。即使现在使用的是maven里面带有的tomcat7插件运行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>

乍一看,这个配置还是蛮复杂的,其实我们常用的没有这么多,而且这些用起来也是非常简单的。在pom.xml文件中,根元素project下的dependencies标签中可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖dependency标签中都应该包含以下元素:
maven快速入门第五讲——依赖的作用范围
很多时候,大部分依赖声明只包含groupId、artifactId和version这三个指定基本坐标的元素;而在一些特殊情况下,其它元素至关重要,也就是上面提到的scope、optional和exclusions。下面我只对scope这个元素进行详细的介绍。

依赖范围

依赖范围是什么?

我们需要知道,maven在编译项目主代码的时候需要使用一套classpath。举例来说:
maven快速入门第五讲——依赖的作用范围
所以,依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系的。

依赖范围具体有哪些?

scope(依赖范围)共有五种,它们分别是compile、provided、runtime、test、system。如果你记不起来这五种依赖范围也没关系,借助eclipse这个IDE就能知晓了,按照下图所示的步骤来操作即可。
maven快速入门第五讲——依赖的作用范围
下面我会一一详细地介绍每一种依赖范围。

compile

编译依赖范围。如果没有指定scope值,那么就会默认使用该依赖。使用该依赖范围的maven依赖,对于编译、测试、运行这三种classpath都有效。

就以前一讲中的maven项目来说,由于没有显示指定scope值,所以下面这两个依赖都是使用的编译依赖范围。
maven快速入门第五讲——依赖的作用范围
如果将该maven项目部署到本地tomcat服务器中,并启动本地tomcat服务器,那么就能在本地tomcat服务器的webapps目录下看到该maven项目了。
maven快速入门第五讲——依赖的作用范围
而且还会发现该maven项目的WEB-INF\lib目录下有如下3个jar包。
maven快速入门第五讲——依赖的作用范围
而我们知道本地tomcat服务器的lib目录中已经存在servlet-api.jar和jsp-api.jar这俩jar包了,所以这个时候就会发生jar包冲突的情况。只要在Google Chrome浏览器中访问HelloServlet,就会报一开始出现的错误。

provided

已提供依赖范围。使用此依赖范围的maven依赖,对于编译和测试classpath有效,但在运行时无效。最典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于tomcat容器已经提供,所以就不需要maven重复地引入一遍了。

还是以前一讲中的maven项目来说,现在我们将下面两个依赖的scope置为provided。也有两种方式来完成这一操作,第一种方式是手动在每一个依赖中添加<scope>provided</scope>

<!-- 添加依赖 -->
<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

第二种方式是直接利用eclipse就可以很方便地为每一个依赖添加<scope>provided</scope>了,大家按照下图所示的步骤进行操作即可。
maven快速入门第五讲——依赖的作用范围
点击OK按钮,再保存一下pom.xml文件,这时你会发现pom.xml文件变成了下面这个样子。
maven快速入门第五讲——依赖的作用范围
再将该maven项目部署到本地tomcat服务器中,并启动本地tomcat服务器,虽然你会发现本地tomcat服务器的webapps目录下还是会有该maven项目,但是该maven项目的WEB-INF目录下直接没有lib目录了。
maven快速入门第五讲——依赖的作用范围
这就说明没有jar包冲突的情况发生了。这时,在Google Chrome浏览器中访问HelloServlet,你就会看到如下图所示的效果了。
maven快速入门第五讲——依赖的作用范围
就算是你现在使用maven里面带有的tomcat7插件来运行maven项目,然后同样在Google Chrome浏览器中访问HelloServlet,你依然会看到同样的结果,报错是不可能的了。

runtime

运行时依赖范围。使用此依赖范围的maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。最典型的例子就是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。

test

测试依赖范围。使用此依赖范围的maven依赖,只对于测试classpath有效,在编译主代码或者运行项目时都无法使用此依赖。最典型的例子就是JUnit,它只有在编译测试代码及运行测试用例的时候才需要。也就是说,在编译和测试时是需要的,但在运行时不需要,因为在测试的时候,我们是用来跑测试用例的,而将来把项目打成jar包或者war包,丢到服务器上去,还要跑测试用例吗?肯定是不需要的。

system

系统依赖范围。该依赖与三种classpath的关系和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此谨慎使用。system的使用例子如下:

<dependencies>
    <dependency>
        <groupId>com.jellythink.BookStore</groupId>
        <artifactId>BookStore-SSO</artifactId>
        <version>1.0</version>
        <scope>system</scope>
        <systemPath>${basedir}/lib/BookStore-SSO-1.0.jar</systemPath>
    </dependency>
</dependencies>

对于system系统依赖范围,在进行以上配置以后,编写代码时已经可以引入jar包中的class了,但是在打包时,由于scope为system,默认并不会将依赖包打进war包中,所以需要通过插件进行打包。例如:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.10</version>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>compile</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/lib</outputDirectory>
                <includeScope>system</includeScope>
            </configuration>
        </execution>
    </executions>
</plugin>

总结

为了更好的理解和记忆依赖范围与classpath的关系,可以将上述内容总结成一张表格。
maven快速入门第五讲——依赖的作用范围