zoukankan      html  css  js  c++  java
  • Maven dependency那些事儿

    身边有几位刚使用Maven的同学表示——在一个叫"pom.xml"的文件里声明一个依赖就不用去手动添加jar了,感觉这东西和自己手动管理依赖没太大区别。
    当然,并不是这样,在此记录dependency那些事儿。

    dependency

    一个依赖可以按照maven的坐标标准进行定义。
    比如:

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


    上面是最常见的坐标属性,偶尔也会看到有classifier
    下面简单说明一下标签:

    • groupId:通常用作项目标识,比如SpringFramework项目的groupId是org.springframework。
      也可能会用作项目隶属的组织的名称,但组织下有多个项目则不好分辨,尽量保证层次级别明确。
    • artifactId:级别低于groupId的项目标识,通常用于标识一个module,比如spring-aop。
    • classifier:用于标识隶属于module的附件。
    • version:当前项目版本,版本的写法应有相应的规范。
    • type:依赖的类型,通常不需要声明,默认为jar。
    • scope:依赖范围,默认为compile。
    • packaging:打包方式,默认为jar。
    • optional:依赖是否可选。
    • exclusions:排除传递性依赖。

    scope

    引入Junit依赖时通常需要声明test scope:

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    

    大概能猜出是测试时使用该依赖,但不完全正确。
    Maven编译代码时需要使用classpath,但classpath不止一种,而是:

    • 编译classpath
    • 测试classpath
    • 运行classpath


    Maven会根据需要使用不同的classpath,而scope可以用来控制依赖与这三种classpath之间的关系。

    • compile:默认使用该项,对三种classpath都有效。
    • test:仅对测试classpath有效,如上面的Junit。
    • provided:对编译和测试classpath有效,比如开头贴出的servlet-api的依赖,会在运行时由app server提供,Maven不可以重复引入。
    • runtime:对测试和运行classpath有效,编译时无效。
    • system:对编译和测试classpath有效,但必须通过systemPath显示指定依赖文件的位置,可以使用系统环境变量。
    • import:不针对任何一种classpath,该项用于导入其他pom中的dependencyManagement元素。


    通常会使用前三种。
    scope不仅用来控制依赖与classpath之间的关系,还会对依赖的传递性产生影响。
    传递性依赖? 比如A依赖B,B依赖C,则A对于B是直接依赖,对于C是传递性依赖。
    A对B、B对C的依赖范围决定了A对C的依赖范围。
    如何决定? 下面给出一个关系表,垂直表示第一依赖,水平表示第二依赖,交叉单元格为传递性依赖。

      compile test provided runtime
    compile compile     runtime
    test test     test
    provided provided   provided provided
    runtime runtime     runtime


    考虑一下这样的依赖关系,A-> C -> D(1.0)和A-> B -> D(2.0)
    此时应该如何处理? 引入两种D依赖是不可能的。
    Maven有依赖调节原则:

    • 路径最近优先。
    • 当路径长度相同,声明顺序优先。

    对于上面的例子,A-> C -> D(1.0)和A-> B -> D(2.0)的路径长度相同,但前者声明早于后者,因此加入的传递性依赖则是D(1.0)。

    另外,还需要考虑这样一个场景。
    A依赖B、B依赖X和Y,X和Y都是可选依赖,即<optional>true</optional>,且4个都是compile。
    此时,X和Y则不会被传递,对A是不可见的。

    exclusions

    但依赖调节并不解决所有问题,我们还需要exclusions来进行排除依赖
    例如这样一个情况,工程中引入了A,A依赖B,但是B的版本过旧。
    此时可以使用exclusions排除该传递性依赖,并显示声明一个最新版本的B依赖。
    比如这样:

    <dependency>
        <groupId>com.lowagie</groupId>
        <artifactId>itext</artifactId>
        <version>4.2.1</version>
        <exclusions>
            <exclusion>
                <artifactId>bcmail-jdk14</artifactId>
                <groupId>bouncycastle</groupId>
            </exclusion>
            <exclusion>
                <artifactId>bcprov-jdk14</artifactId>
                <groupId>bouncycastle</groupId>
            </exclusion>
            <exclusion>
                <artifactId>bctsp-jdk14</artifactId>
                <groupId>bouncycastle</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    

    properties

    用于统一管理属性,比如我们引入很多spring framework:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.0.3 RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.0.3 RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>4.0.3 RELEASE</version>
    </dependency>
    

    可见版本都是一样的,想更改版本时再一个一个修改太麻烦。


    properties可以解决这一问题:

    <properties>    
        <spring.version>4.0.3.RELEASE</spring.version>
    </properties>
    


    引入spring可以改为:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>${spring.version}</version>
    </dependency>
    

    这样确实简介了不少,但你可能仍然讨厌XML。

    兴许,以后我们不会再用XML写构建文件。
    我们可能会用一些插件(比如Polyglot for Maven)或者其他的什么东西(比如Gradle)。

    repository

    可以把Maven的仓库分为两种:

    • 本地仓库
    • 远程仓库

    Maven寻找一个dependency时会先从本地仓库查找,如果找不到则在远程仓库查找,发现则下载到本地仓库使用。
    如果都查找失败,会提示build failure。

    或者,我们也可以把本地的jar放到本地仓库中:

    mvn install:install-file -Dfile=jar包的路径 -DgroupId=我的groupId -DartifactId=我的artifactId -Dversion=我的version -Dpackaging=jar
    


    本地仓库默认路径是用户目录下的.m2/repository/
    该路径可以在settings.xml中修改,比如:

    <localRepository>
        /usr/local/maven/repository
    </localRepository>
    


    那么远程仓库又在哪里?
    打开$M2_HOME/lib/maven-model-builder-3.2.1.jar里的 org/apache/maven/model/pom-4.0.0.xml

    看到远程仓库的设置如下:

     <repositories>
        <repository>
            <id>central</id>
            <name>Central Repository</name>
            <url>http://repo.maven.apache.org/maven2</url>
            <layout>default</layout>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
     </repositories>
    


    当然,我们也可以配置其他远程仓库,比如这样:

    <repositories>
        <repository>
            <id>opensesame</id>
            <name>Alibaba OpenSource Repsoitory</name>
            <url>http://code.alibabatech.com/mvn/releases/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    

    配置远程仓库时也需要注意一些选项

    • id:仓库的唯一标识符
    • name:仓库名称
    • url:仓库地址,通常都是http协议
    • layout:默认为default,表示仓库布局为maven2和maven3的布局
    • release/snapshots
      • enabled:是否支持发布版/快照版
      • updatePolicy:更新策略
        • 默认为daily
        • always : 每次构建时检查
        • never : 从不检查
        • interval : X:每隔X分钟间差
      • checksumPolicy:校验策略,默认为warn,另外有failignore


    为什么区分release和snapshot? 不能只通过版本号进行区分吗?

    试想一下这样的场景,假设有A和B两个模块,A依赖B,且B尚未开发完成。
    如何让B模块每次更新后让A的开发人员获取?
    每次更新后提示A的开发人员从VCS上pull下来构建?
    或者不停地换版本号? 确实,就算B有了变化,但version也是依赖的标识之一。

    如果依赖是snapshot则能解决这样的问题,snapshot发布时会加上一个时间戳,每次构建A的时候会检查B是否最新,间差更新策略就是上面的updatePolicy
    另外,也可以执行mvn clean install-U强制更新。


    远程仓库不都是想访问就访问的,有些仓库出于安全考虑,需要提供认证信息才可以访问。
    认证必须在settings.xml中设置,下面是一个例子:

    <servers>
       <server>
            <id>server001</id>
            <username>my_login</username>
            <password>my_password</password>
            <privateKey>${user.home}/.ssh/id_dsa</privateKey>
            <passphrase>some_passphrase</passphrase>
            <filePermissions>664</filePermissions>
            <directoryPermissions>775</directoryPermissions>
            <configuration></configuration>
        </server>
    </servers>
  • 相关阅读:
    react路由组件&&非路由组件
    react函数式组件(非路由组件)实现路由跳转
    react使用antd组件递归实现左侧菜单导航树
    【LeetCode】65. Valid Number
    【LeetCode】66. Plus One (2 solutions)
    【LeetCode】68. Text Justification
    【LeetCode】69. Sqrt(x) (2 solutions)
    【LeetCode】72. Edit Distance
    【LeetCode】73. Set Matrix Zeroes (2 solutions)
    【LeetCode】76. Minimum Window Substring
  • 原文地址:https://www.cnblogs.com/jinkora/p/4381103.html
Copyright © 2011-2022 走看看