maven使用经验总结

maven是java开发常用的构建工具,maven的依赖管理、冲突排包等工作又比较耗时费力,因此了解maven原理,熟悉maven的使用,可以有效提高开发人员的工作效率。本文通过介绍maven的原理,总结日常使用经验,分享出来,希望对大家有所帮助。

核心概念

学习和使用maven可以从两个配置文件入手:settings.xml 和pom.xml。其中settings.xml主要管理mvn仓库的相关配置,通常放在.m2/settings.xml文件。pom.xml主要管理项目的依赖和构建配置。

settings.xml

重点关注以下配置:

server: 指定远程maven仓库的连接用户和密码,示例如下。



  deploymentRepo
  
  repouser
  repopwd

repository: 设置远程仓库的id,地址以及下载的文件类型和更新策略,示例如下。实际使用过程中有时候会碰到SNAPSHOT依赖包不会自动更新,可以检查这个配置的updatePolicy值,改为always即可。


      jdk-1.4

      
        1.4
      

      
        
        
          jdk14
          Repository for JDK 1.4 builds
          
          http://www.myhost.com/maven/jdk14
          
              false
              
              always
            
            
              true
            
        
      
    

maven是java开发常用的构建工具,maven的依赖管理、冲突排包等工作又比较耗时费力,因此了解maven原理,熟悉maven的使用,可以有效提高开发人员的工作效率。本文通过介绍maven的原理,总结日常使用经验,分享出来,希望对大家有所帮助。

核心概念

学习和使用maven可以从两个配置文件入手:settings.xml 和pom.xml。其中settings.xml主要管理mvn仓库的相关配置,通常放在.m2/settings.xml文件。pom.xml主要管理项目的依赖和构建配置。

settings.xml

重点关注以下配置:

server: 指定远程maven仓库的连接用户和密码,示例如下。





deploymentRepo



repouser

repopwd

repository: 设置远程仓库的id,地址以及下载的文件类型和更新策略,示例如下。实际使用过程中有时候会碰到SNAPSHOT依赖包不会自动更新,可以检查这个配置的updatePolicy值,改为always即可。



jdk-1.4



1.4









jdk14

Repository for JDK 1.4 builds



http://www.myhost.com/maven/jdk14



false



always





true







pom.xml

重点关注以下配置:

project: pom文件的根元素。核心有groupId、artifactId、version、packaging几个元素。其中groupId、artifactId、version指定了项目的唯一坐标。packaging指定了项目的打包类型,,即项目通过maven打包的输出文件的后缀名,包括jar、war、ear、pom等。

parentparent: pom文件通过parent来实现继承。同样包括groupId、artifactId、version三个元素,用来唯一指定继承的父pom文件的坐标。其次还有relativePath指定父pom.xml文件的相对位置,默认文件位置是../pom.xml。

build: 构建信息,主要包括项目构建所需路径管理,资源管理,插件管理元素等。

  1. 其中路径管理包括源码文件路径,测试代码路径,输出文件路径,脚本源码路径等。
  2. 资源管理包括资源文件的源目录,资源构建的模板路径。其中filtering指定是否使用参数值代替参数名,参数值取自文件里配置的属性。实际使用中可以通过filtering元素来动态替换配置文件中的配置值,比如不同环境使用不同的jdbc连接地址,可以通过filtering来实现。
  3. 插件管理元素包括项目中可以使用的mvn插件的声明以及相关配置。通常在父pom文件的pluginManagement声明使用的插件,子pom文件的plugin元素真正引入这些插件,并且可以对使用的插件配置进行覆盖。常用的插件有
  • java编译插件maven-compiler-plugin,
  • 资源打包插件maven-resources-plugin,
  • 源码打包插件maven-source-plugin

这些配置都有构建执行阶段phase, 执行目标goal等配置。常见的构建生命周期如下

阶段 处理 描述
验证 validate 验证项目 验证项目是否正确且所有必须信息是可用的
编译 compile 执行编译 源代码编译在此阶段完成
测试 Test 测试 使用适当的单元测试框架(例如JUnit)运行测试。
包装 package 打包 创建JAR/WAR包如在 pom.xml 中定义提及的包
检查 verify 检查 对集成测试的结果进行检查,以保证质量达标
安装 install 安装 安装打包的项目到本地仓库,以供其他项目使用
部署 deploy 部署 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程

maven除了构建生命周期外,还有clean:项目清理生命周期,site: 项目文档的生命周期,有兴趣的同学可以上网查资料,这里不再赘述。

dependencies: 依赖管理是pom文件最常用的功能。

这里要注意dependencyManagementdependencies的关系:

  1. dependencyManagement里只是声明依赖,并不真正引入。子项目需要显式的声明需要用的依赖并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都从父pom继承;如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
  2. 可以通过dependencyManagement实现pom依赖的多继承。子pom文件只能继承一个父pom文件,要想实现多继承,可以在父pom中指定依赖类型pom来继承三方pom中的依赖,示例如下
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
    

    排包

    使用maven过程中,经常会碰到 ClassNotFound或者MethodNotFound这样的异常,通常都是依赖包冲突引起的。解决冲突很简单,可以参考冲突解决,难点在于如何定位到冲突包的位置。关键是要理解maven依赖的引用顺序原则:最短路径,优先声明,依赖覆盖

    加载顺序

    最短路径

    比如测试项目test依赖了A.jar, 又依赖了B.jar的1.0版本,同时A.jar也依赖了B.jar的2.0版本,那么实际使用的应该是B.jar的1.0版本。原因是test->B.jar 1.0 的路径长度小于test -> A.jar -> B.jar 2.0的路径长。如图

    暂时无法在文档外展示此内容

    优先声明

    如果依赖路径长度都相同,那么先声明的依赖优先加载。示例如下

    暂时无法在文档外展示此内容

    test模块到c.jar 1.0 和 c.jar 2.0的路径长度一样,那么优先加载哪个版本?取决于pom文件中A.jar, B.jar的声明顺序。如果先声明了A,则优先使用C.jar 2.0。即先声明的优先生效。

    
                com.xxx
                A
                1.0
            
            
                com.xxx
                B
                1.0
            

    依赖覆盖

    pom文件中,两个相同的依赖,即groupId, artifactId相同的依赖,后声明的会覆盖先声明的。示例如下,同时声明了两个A.jar依赖,后面声明的生效,最终加载1.0版本。

    
                com.xxx
                A
                2.0
            
            
                com.xxx
                A
                1.0
            

    了解maven依赖的顺序原则后,再来看如何定位包冲突。包冲突的表现都差不多,系统启动或者运行时抛出ClassNotFound或者MethodNotFound,如下图:

    Description:
    
    An attempt was made to call a method that does not exist. The attempt was made from the following location:
    
        org.apache.dubbo.rpc.cluster.Cluster.getCluster(Cluster.java:58)
    
    The following method did not exist:
    
        org.apache.dubbo.common.extension.ExtensionLoader.getExtension(Ljava/lang/String;Z)Ljava/lang/Object;
    
    The method's class, org.apache.dubbo.common.extension.ExtensionLoader, is available from the following locations:
    
        jar:file:/Users/admin/.m2/repository/org/apache/dubbo/dubbo-common/2.7.7/dubbo-common-2.7.7.jar!/org/apache/dubbo/common/extension/ExtensionLoader.class
        jar:file:/Users/admin/.m2/repository/org/apache/dubbo/dubbo/dewu-2.7.7/dubbo-dewu-2.7.7.jar!/org/apache/dubbo/common/extension/ExtensionLoader.class
    
    It was loaded from the following location:
    
        file:/Users/admin/.m2/repository/org/apache/dubbo/dubbo-common/2.7.7/dubbo-common-2.7.7.jar
    
    
    Action:
    
    Correct the classpath of your application so that it contains a single, compatible version of org.apache.dubbo.common.extension.ExtensionLoader

    方法org.apache.dubbo.common.extension.ExtensionLoader.getExtension(Ljava/lang/String;Z)Ljava/lang/Object;不存在。这里使用JNI描述符描述,表示ExtensionLoader类的方法 Object getExtension(String, boolean)找不到。同时指出了冲突的两个jar:dubbo-common-2.7.7.jar 和 dubbo-dewu-2.7.7.jar, 以及真正加载的jar是dubbo-common-2.7.7.jar。

    JNI描述符

    JNI描述符是 JNI 对java 类型的编码,如

    Ljava/lang/String;表示String 类型, Z表示布尔类型等等。

    具体参考JNI描述符

    通常依赖冲突都是由相同jar包的不同版本引起的,这类冲突可以通过maven命令 mvn dependency:tree|grep [artifactId] 查看引入该jar的依赖路径,从而排查冲突,如下

    也可以通过maven helper插件快速定位冲突

    冲突解决

    根据依赖包的加载顺序的三个原则,我们可以有以下方式来解决冲突通过明确指定依赖包版本使用exclusions元素排查冲突的包,如下

    
        com.alibaba.nacos
        nacos-client
        1.4.0
        
            
                com.fasterxml.jackson.core
                jackson-databind
            
        
    

  3. 通过移动依赖声明的顺序,把需要加载的依赖包提前,保证加载正确的版本(不推荐)。若非必要,不要依赖声明顺序来指定加载顺序。
  4. 使用规范

  5. 线上依赖jar包不要使用SNAPSHOT包。
  6. 说明:SNAPSHOT包即快照包是不稳定包,mvn仓库不会限制快照包的部署次数,提供方可能会随时修改包内容并重新发布,此时线上如果引用了快照包可能会触发接口兼容异常,导致线上故障。

  7. SNAPSHOT依赖设置为自动刷新
  8. 说明:开发过程中经常碰到SNAPSHOT包不更新导致接口兼容问题,排查起来耗时费力。可以通过设置SNAPSHOT依赖自动刷新来解决。部署SNAPSHOT包时,使用-u命令强制刷新所有依赖:mvn deploy -U

  9. settings.xml文件设置SNAPSHOT的依赖刷新策略为always,保证SNAPSHOT依赖包能够及时刷新。

    
        
          xxx
          xxxx name
          
          
              false
              always
            
        
    

  10. 开发测试SNAPSHOT包时,优先发布到本地。
  11. 通过在父项目中定义好 groupId ,version 等参数, 子项目不用定义这些参数,优先从父项目继承。
  12. 依赖尽量通过dependencyManagement提前在父POM中声明,项目中省略版本号设置。
  13. 优先使用import pom的方式管理依赖集合。可以有效减少pom文件的大小,屏蔽依赖包的复杂度。比如Spring boot的相关依赖可以通过以下方式管理
  14. 说明:开发测试SNAPSHOT时,会频繁修改包内容,可能影响其他依赖了该SNAPSHOT包的项目。因此开发测试该包时,先通过mvn install部署到本地仓库,本地自测确认没有兼容问题后再通过mvn deploy部署到远端仓库。

    
        
          
            org.springframework.boot
            spring-boot-dependencies
            1.5.4.RELEASE
            pom
            import
          
        
    
    
    

你可能感兴趣的:(maven,java,前端)