---
# System prepended metadata

title: Maven
tags: [Maven]

---

# Maven
# Maven的Project管理
## optional元素
- optional元素设置为true表示何意？optional是Maven依赖jar时的一个选项，表示该依赖是可选的，项目之间依赖不传递。不设置optional（默认）或者optional是false，表示传递依赖。
- 这里假设有两个项目A和B，其中A为父项目，B为子项目。当父项目添加junit依赖时，并未添加optional选项，也就是默认的optional元素的值为false。此时，子项目依赖父项目。
    - 父項目A
    ```pom=
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    ```
    - 子項目B
    ```pom=
    <dependencies>
        <dependency>
            <grouiesId>A</groupId>
            <artifactId>A</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    ```
    - 父项目并未设置optional元素为true，那么便具有依赖传递性。此时，子项目B中会直接引入父项目A中引入的Junit的jar包。也就是说B项目打包时，jar/war包中会包含junit的jar包。
## scope元素
- scope元素主要用来控制依赖的使用范围，指定当前包的依赖范围和依赖的传递性，也就是哪些依赖在哪些classpath中可用。常见的可选值有：compile, provided, runtime, test, system等。
### compile
- 默认值。compile表示对应依赖会参与当前项目的编译、测试、运行等。打包时通常会包含该依赖，部署时会打包到lib目录下。
### test
- 只有在單元測試時會用到，發佈程式時並不會用到，所以不會被打包
- 最常见的使用就是单元测试类了。

```pom=
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
```
### runntime
- 在编译环境下不会被使用，适用于运行和测试环节，發佈程式時須要一併打包。比如编译时只需要JDBC API的jar，而只有运行时才需要JDBC驱动实现。
    - JDBC API的jar在JRE Syetem Library(由JDK提供)可以找到，就是java.sql。
    - [Java JDBC API](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/)
- JDBC驱动实现
```pom=
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.20</version>
    <scope>runtime</scope>
</dependency>
```
### provided
- 仅在编译和测试阶段生效，不会被打包，也不具有传递性。
### system
- system范围依赖与provided类似，不过依赖项不会从maven仓库获取，而需要从本地文件系统提供。使用时，一定要配合systemPath属性。不推荐使用，尽量从Maven库中引用依赖。
```pom=
<dependency>
  <groupId>sun.jdk</groupId>
  <artifactId>tools</artifactId>
  <version>1.5.0</version>
  <scope>system</scope>
  <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
```
### import
- 它只使用在`<dependencyManagement>`中，表示从其它的pom中导入dependency的配置。
- 大家在做SpringBoot应用的时候，都会有如下代码：
    ```pom=
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>
    ```
    - 继承一个父模块，然后再引入相应的依赖。假如说，我不想继承，或者我想继承多个，怎么做？
- Maven的继承和Java的继承一样，是无法实现多重继承的。
- 你可以把dependencyManagement放到单独的专门用来管理依赖的pom中，然后在需要使用依赖的模块中通过import scope依赖，就可以引入dependencyManagement。例如可以写这样一个用于依赖管理的pom：
    ```pom=
    <project>
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.test.sample</groupId>
        <artifactId>base-parent1</artifactId>
        <packaging>pom</packaging>
        <version>1.0.0-SNAPSHOT</version>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>junit</groupId>
                    <artifactid>junit</artifactId>
                    <version>4.8.2</version>
                </dependency>
                <dependency>
                    <groupId>log4j</groupId>
                    <artifactid>log4j</artifactId>
                    <version>1.2.16</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    </project>
    ```
- 然后就可以通过非继承的方式来引入这段依赖管理配置
    ```pom=
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.test.sample</groupId>
                <artifactid>base-parent1</artifactId>
                <version>1.0.0-SNAPSHOT</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactid>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactid>log4j</artifactId>
        </dependency>
    </dependencies>
    ```
- 如何解决一開始討論的SpringBoot的继承问题?
    ```pom=
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    ```
## dependencyManagement
- 在父項目中
    ```pom=
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connectoer-java</artifactId>
                <version>5.1.44</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    ```
- 在子項目中只需要` <groupId>`和` <artifactId>`即可，如
```pom=
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connectoer-java</artifactId>
    </dependency>
</dependencies>
```
- 使用dependencyManagement可以统一管理项目的版本号，确保应用的各个项目的依赖和版本一致，不用每个模块项目都弄一个版本号，不利于管理，当需要变更版本号的时候只需要在父类容器里更新，不需要任何一个子项目的修改。如果某个子项目需要另外一个特殊的版本号时，只需要在自己的模块dependencies中声明一个版本号即可。子类就会使用子类声明的版本号，不继承于父类版本号。
- 与dependencies区别：
    - Dependencies相对于dependencyManagement，所有生命在dependencies里的依赖都会自动引入，并默认被所有的子项目继承。
    - dependencyManagement里只是声明依赖，并不自动实现引入，因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖，是不会从父项目中继承下来的；只有在子项目中写了该依赖项，并且没有指定具体版本，才会从父项目中继承该项，并且version和scope都读取自父pom;另外如果子项目中指定了版本号，那么会使用子项目中指定的jar版本。




# 例子
- 以cub-boot為例
- 我們的專案引入cub-boot
    ```pom=
    <groupId>com.example</groupId>
	<artifactId>checkId</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>checkId</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
    <parent>
        <groupId>com.cathaybank.boot</groupId>
        <artifactId>cub-boot</artifactId>
        <version>1.1.2-RELEASE</version>
    </parent>
    ```
    - cub-boot中已經寫過的dependency，我們專案的pom就不需要再寫了，比如cub-boot-autoconfigure。
    
- cub-boot
    - cub-boot-dependencies是用來做依賴的版本管控，所以在cub-boot裡看到的依賴也都不用寫版本號。
    ```pom=
    <name>Chris</name>
    <artifactId>cub-boot</artifactId>
    <packaging>pom</packaging>
    <parent>
        <groupId>com.cathaybank.boot</groupId>
        <artifactId>cub-boot-dependencies</artifactId>
        <version>1.1.2-RELEASE</version>
        <relativePath />
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>com.cathaybank.boot</groupId>
            <artifactId>cub-boot-autoconfigure</artifactId>
        </dependency>
        .....
    </dependencies>
    ```
- cub-boot-autoconfigure
    ```pom=
    <parent>
        <groupId>com.cathaybank.boot</groupId>
        <artifactId>cub-boot-dependencies</artifactId>
        <version>1.1.2-RELEASE</version>
        <relativePath />
    </parent>

    <artifactId>cub-boot-autoconfigure</artifactId>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-core</artifactId>
            <scope>provided</scope>
        </dependency>
        .....
    </dependencies>
    ```
    - 若引入的dependency的scope是provided，那表示只有在編譯和測試時會被使用，不會被打包，也不具有傳遞性。(因為不會被打包)所以在cub-boot-autoconfigure打包成jar後，lib裡面不會包含那些provided的依賴，(因為不具有傳遞性)我們的checkId專案打包之後，lib裡也不會有cub-boot-autoconfigure中scope是provided的依賴。
    - 如何看到專案打包後，包含的依賴?
        - 對checkId專案右建Run -> Maven Install -> target資料夾找到已打包好的專案checkId-0.0.1-SNAPSHOT.jar -> 將該jar檔複製一份到桌面並解壓縮 -> checkId-0.0.1-SNAPSHOT\BOOT-INF\lib中就可以看到包含了哪些依賴

- cub-boot-dependencies
    ```pom=
    <groupId>com.cathaybank.boot</groupId>
    <artifactId>cub-boot-dependencies</artifactId>
    <packaging>pom</packaging>
    <version>1.1.2-RELEASE</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.2.6.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <swagger.version>2.9.2</swagger.version>
        .......
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <dependency>
                <groupId>com.cathaybank.boot</groupId>
                <artifactId>cub-boot-autoconfigure</artifactId>
                <version>${cub.boot.version}</version>
            </dependency>
            ......
        </dependencies>
    </dependencyManagement>
    ```


# 參考
[Maven中optional和scope元素的使用，你弄明白了？](https://cloud.tencent.com/developer/article/1756145)
[Maven教學](https://kentyeh.github.io/mavenStartup/index.html)
[Maven依赖中的Scope详解](https://segmentfault.com/a/1190000038594247)
[Maven中dependencyManagement作用说明](https://blog.csdn.net/vtopqx/article/details/79034835)

###### tags: `Maven`