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>
  • 相关阅读:
    httpclient_1
    jmeter java请求
    fiddler监听手机
    lr文件的作用?
    配置源
    数据结构--堆
    A + B Problem II 高精度
    最小生成树(kruskal算法)+prim算法
    P3371 【模板】单源最短路径(弱化版)
    P3368 【模板】树状数组 2(实现区间修改&单点查询)
  • 原文地址:https://www.cnblogs.com/jinkora/p/4381103.html
Copyright © 2011-2022 走看看