Maven依赖理解

  • 1 简介
  • 2 依赖的配置
  • 3 依赖的范围
  • 4 传递性依赖
  • 4.1 传递性依赖和依赖调解
  • 5 依赖调解
  • 6 可选依赖
  • 7 总结
  • 8 下载


1 简介

 在Maven项目中,有一个核心文件pom.xml。POM项目对象模型定义了项目的基本信息,用于描述心目如何构建,声明项目依赖。
 没有任何的实际Java代码,我们就能顶一个Maven项目的POM,这体现了Maven的一大优点,它能让项目对象模型最大程度地与实际代码相互独立,即解耦,或者正交性。这在很大程度上避免了Java代码和POM代码的相互影响。比如当项目需要升级版本时,只需要修改POM,而不需要更待Java代码;而在POM稳定之后,日常的Java代码开发工作基本不涉及POM的修改。
下面的pom文件即为一个xml

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>


    <groupId>com.example</groupId>
    <artifactId>screenshot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>screenshot</name>
    <description>Demo project for Spring Boot</description>
    <packaging>war</packaging>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 移除嵌入式tomcat插件 -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>screenshot</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <skip>true</skip>
                    <encoding>UTF-8</encoding>
                    <compilerArguments>
                        <extdirs>${project.basedir}/src/main/resources/lib</extdirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <webResources>
                        <resource>
                            <directory>src/main/resources/lib</directory>
                            <targetPath>WEB-INF/lib</targetPath>
                            <includes>
                                <include>**/*.jar</include>
                            </includes>
                        </resource>
                        <resource>
                            <directory>src/main/java/com/cetc52/screenshot/sdk</directory>
                            <targetPath>WEB-INF/sdk</targetPath>
                        </resource>
                    </webResources>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

在POM文件中标签下的每个即表示该项目的一个直接依赖。

2 依赖的配置

 从上述的简单pom.xml文件中,可以看到Maven项目的依赖声明如下:

<project>
	<dependencies>
            <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter</artifactId>
				  <type>…</type>
				  <scope>…</scope>
				  <optional>…</optional>
				  <exclusions>
				    	<exclusion>…</ exclusion>
				    	<exclusion>…</ exclusion>
				  </exclusions>
            </dependency>
    </dependencies>
</project>

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

  1. groupId, artifactId, version具体含义同Maven坐标,对于任何一个依赖,使用Maven坐标机制找打准确的依赖。
  2. type依赖的类型,对英语项目坐标定义的packaging,大部分情况下,元素不必填写,默认为jar
  3. scop依赖范围
  4. optional标记依赖是否可选
  5. exclusions用来排除传递依赖

3 依赖的范围

Maven在编译项目、测试项目、以及运行项目时使用三套classpath。编译时classpath为src/main/resources,测试时classpath为src/test/resources,在运行时classpath则为target/classes。

依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系。

mvn 输出maven 依赖信息 maven依赖写法_spring


不考虑import依赖范围,其他各种依赖范围与三种classpath的关系参见Maven依赖范围与classpath、依赖传递如图:

mvn 输出maven 依赖信息 maven依赖写法_Maven_02

4 传递性依赖

 Maven在帮助开发人员根据POM下载依赖时,会自动解析Maven项目的依赖,包括直接在POM文件中定义的直接依赖,以及每个依赖依赖的其他依赖。这个过程会自动进行,不需要手动干预。
spring-core有一个依赖commons-logging依赖。

<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>

 该依赖没有声明依赖范围,那么其依赖范围就是默认的compile。因此依赖图示如下:

mvn 输出maven 依赖信息 maven依赖写法_spring_03


 通过传递性依赖机制,Maven项目就不需要考虑它依赖了什么,也不用担心引入多余的依赖。Maven会解析各个直接的POM,将那些必要的间接依赖以传递性依赖的形式引入到当前项目中。

4.1 传递性依赖和依赖调解

 假设A依赖B,B依赖C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖。A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。下表中,最左边表示第一直接依赖范围,最上边为第二直接依赖范围,中间的交叉单元格则表示传递性依赖范围。

mvn 输出maven 依赖信息 maven依赖写法_Maven_04


 通过查看上图,可以发现这样的规律,当第二直接依赖的范围为compile的时候,传递性依赖范围与第一直接依赖的范围一致。当第二直接依赖的范围为test的时候,依赖不会传递;当第二直接依赖的范围为是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖范围同样为provided;当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖一致,但compile例外,此时传递性依赖的范围为runtime

5 依赖调解

 大部分情况下,我们只需要关心项目的直接依赖是什么,而不用考虑这些依赖会引入什么传递性依赖,当传递性依赖造成问题的时候,则需要清楚的知道该传递性依赖是从那条依赖路径引入的。

mvn 输出maven 依赖信息 maven依赖写法_spring_05

6 可选依赖

 一种场景是A–>B, B–>X(可选),B–>Y(可选),如果这三个依赖的范围都是compile,那么X、Y、就是A的compile范围传递性依赖。然而由于这里X、Y是可选依赖,依赖不会传递。换句话说X、Y不会对A有任何影响。

项目B实现了两个特性,其中一项特性依赖X,另一项特性依赖Y,而且两个特性是互斥的。

mvn 输出maven 依赖信息 maven依赖写法_spring_06


mvn 输出maven 依赖信息 maven依赖写法_Maven_07


 上述图片中,使用表示依赖是可选的。它们zhi 对当前项目B产生影响,当其他项目需要依赖B的时候,这两个依赖不会传递,因此,当项目A依赖于项目B的时候,如果其实际使用MySQL数据库,那么在项目A中就必须显式的声明mysql-connector-java这个依赖。

 在理想的情况下,是不应该使用可选依赖的。使用可选依赖的原因是一个项目实现了多个特性,在面向对象设计中,存在单一职责原则,意为一个类应该只有一个原则,而不是糅合太多的功能,这个原则在规划Maven项目的时候同样适用。

7 总结

 本文章总结了Maven实战p62—p68的内容。主要内容包括Maven项目中依赖声明的格式,依赖范围的含义传递性依赖和依赖范围,以及依赖调解的两个原则。最后对于可选依赖的使用进行了简要的阐述,希望能够通过该文档增加对依赖理解的深度。