Maven Plugin

前言


在阅读详细说明文档前我们先来谈谈对Maven的一些理解,有助于从全局角度上了解Maven的作用与意义。

  • Maven是什么?通俗来讲,Maven有助于构建工程、管理Jar包、编译代码,还能自动运行单元测试、打包、生成报表,甚至能帮你部署项目。
  • 使用Maven构建的项目均可以直接使用maven build完成项目的编译、测试、打包,无需额外配置。
  • Maven是通过pom.xml来执行任务的,其中的build标签描述了如何编译打包项目,而具体的编译打包工作是通过build标签中配置的 plugin 来完成的。当然plugin配置不是必须的,默认情况下,Maven 会绑定以下几个插件来完成基本操作。
Plugin function Lifecycle Phase
maven-clean-plugin 清理上一次执行创建的目标文件 clean
maven-resources-plugin 处理源资源文件、测试资源文件 resources,testResources
maven-compiler-plugin 编译源文件、测试源文件 compiler,testCompiler
maven-surefire-plugin 执行测试文件 test
maven-jar-plugin 创建jar package
maven-install-plugin 安装jar,将创建生成的jar拷贝到.m2/repository下面 install
maven-deploy-plugin 发布jar deploy

即使在没有配置的情况下,执行mvn clean install时,maven会调用默认的plugin来完成编译打包操作,具体来讲,执行mvn clean install时会执行plugin

 maven-clean-plugin:2.5:clean (default-clean)
 maven-resources-plugin:2.6:resources (default-resources)
 maven-compiler-plugin:3.1:compile (default-compiler)
 maven-resources-plugin:2.6:testResources (default-testResources)
 maven-compiler-plugin:3.1:testCompile (default-testCompile)
 maven-surefire-plugin:2.12.4:test (default-test)
 maven-jar-plugin:2.4:jar (default-jar)
 maven-install-plugin:2.4:install (default-install)
  • 如果有需要,可以针对各个 plugin 进行特殊配置,需要在pom.xml中的标签中显示指定 plugin 和 属性配置。

    
        
            org.apache.maven.plugins
            maven-compiler-plugin
            3.1
            
                1.8
                1.8
            
        
    

如上配置了maven-compiler-plugin的版本和编译时使用的jdk版本。

配置项详解


1、build标签

Mavenpom.xml文件中,Build相关配置包含两个部分:一个是,另一个是,这里我们只介绍

Mavenpom.xml文件中,存在如下两种标签:

说明:

  • 一种被称为Project Build,即是的直接子元素。另一种被称为Profile Build,即是的直接子元素。
  • Profile Build包含了基本的build元素,而Project Build还包含两个特殊的元素,即各种<...Directory>

1.1、Profile Build和Project Build的共有元素

1.1.1、公用的基本build元素

说明:

  • defaultGoal,执行构建时默认的goalphase,如jar:jar或者package
  • directory,构建的结果所在的路径,默认为${basedir}/target目录
  • finalName,构建的最终结果的名字,该名字可能在其他plugin中被改变
1.1.2 标签

资源往往不是代码,而是一些propertiesXML配置文件,无需编译,构建过程中会往往会将资源文件从源路径复制到指定的目标路径。

给出各个资源在Maven项目中的具体路径。示例如下:

说明:

  • resourcesbuild过程中涉及的资源文件
  • targetPath,资源文件的目标路径
  • filtering,构建过程中是否对资源进行过滤,默认false
  • directory,资源文件的路径,默认位于${basedir}/src/main/resources/目录下
  • includes,一组文件名的匹配模式,被匹配的资源文件将被构建过程处理
  • excludes,一组文件名的匹配模式,被匹配的资源文件将被构建过程忽略。同时被includesexcludes匹配的资源文件,将被忽略。
  • filters,给出对资源文件进行过滤的属性文件的路径,默认位于${basedir}/src/main/filters/目录下。属性文件中定义若干键值对。在构建过程中,对于资源文件中出现的变量(键),将使用属性文件中该键对应的值替换。
  • testResourcestest过程中涉及的资源文件,默认位于${basedir}/src/test/resources/目录下。这里的资源文件不会被构建到目标构件中。
1.1.3、标签

给出构建过程中所用到的插件。

说明:

  • groupId
  • artifactId
  • version
  • extensions,是否加载该插件的扩展,默认false
  • inherited,该插件的configuration中的配置是否可以被(继承该POM的其他Maven项目)继承,默认true
  • configuration,该插件所需要的特殊配置,在父子项目之间可以覆盖或合并
  • dependencies,该插件所特有的依赖类库
  • executions,该插件的某个goal(一个插件中可能包含多个goal)的执行方式。一个
    execution有如下设置:
    • id,唯一标识
    • goals,要执行的插件的goal(可以有多个),如run
    • phase,插件的goal要嵌入到Mavenphase中执行,如verify
    • inherited,该execution是否可被子项目继承
    • configuration,该execution的其他配置参数
1.1.4、标签

中,并列,两者之间的关系类似于之间的关系。中也配置,其配置参数与中的完全一致。只是,往往出现在父项目中,其中配置的往往通用于子项目。子项目中只要在中以声明该插件,该插件的具体配置参数则继承自父项目中对该插件的配置,从而避免在子项目中进行重复配置。

1.2、Project Build特有的<…Directory>

往往配置在父项目中,供所有父子项目使用。示例如下:

1.3、Project Build特有的

是执行构建过程中可能用到的其他工具,在执行构建的过程中被加入到classpath中。

也可以通过激活构建插件,从而改变构建的过程。

通常,通过给出通用插件的一个具体实现,用于构建过程。

的使用示例如下:

默认Maven输出目录


构建Maven项目的时候,如果没有进行特殊的配置,Maven会按照标准的目录结构查找和处理各种类型文件。

1、src/main/java和src/test/java

这两个目录中的所有*.java文件会分别在compiletest-compile阶段被编译,编译结果分别放到了target/classestarge/test-classes目录中,但是这两个目录中的其他文件都会被忽略掉。

2、src/main/resouces和src/test/resources

这两个目录中的文件也会分别被复制到target/classestarget/test-classes目录中。

当是web项目时,会在target下生成myproject目录,myproject是你的项目名

1、src/main/webapps

这个目录中的文件会被复制到target/myProject目录中

2、target/classes

默认会把这个目录中的所有内容复制到target/myProject/WEB-INF/classes目录中

3、 Dependency

默认会将项目的依赖复制到target/myProject/WEB-INF/lib

Maven Shade 插件详解

1、介绍

This plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade - i.e. rename - the packages of some of the dependencies.

maven-plugin-shade 插件提供了两个能力:

  • 把整个项目(包含它的依赖)都打包到一个 “uber-jar” 中
  • shade - 即重命名某些依赖的包

由此引出了两个问题:

  • 什么是 uber-jar
    uber-jar 也叫做 fat-jar 或者 jar-with-dependencies,意思就是包含依赖的 jar
  • 什么是 shade
    shade 意为遮挡,在此处可以理解为对依赖的 jar 包的重定向(主要通过重命名的方式)。

Shade 插件只有一个目标:将shade:shade 绑定到 package 阶段,用于创建 Uber jar。问题来了,Uber jar 是什么jar?请看如下介绍。

1.1、Jar 类型

Java 中,如果要运行 Java 程序,需要一个包含 main 方法类的 jar 包或类文件,然后执行命令:

#Run a class file
java $class

#Run a java package (main class specified in manifest)
java -jar $jarfile

#Run the main method of a class, and add multiple jar packages to the classpath of the runtime
java -cp (- classpath) $path (directory/jar file /zip file) # zip file should conform to the specification of jar format

这种方法会有一些缺点,因为大多数 Java 程序都包含很多依赖项。如果要启动此程序,必须传递 classpath 以指定这些依赖的包文件,并且必须在服务器上指定类路径,这不是很方便,特别是随着 DevOps/Microservices 的普及,这种指定 classpath 的方法过于死板。我们可以直接构建聚合的 jar 包并发布或运行它。

1.2、Executable Jar

可执行 jar 包通常意味着所有依赖的 jar 包都放在一个大的入口 jar 包中。这个入口 jar 包中包含运行时需要依赖的所有 jar 包和类文件。您可以将依赖的 jar 包直接放在入口 jar 包中,如下所示:

├─executable jar
│  ├─META-INF
│  │  ├─MANIFEST.MF
│  ├─com...
│  ├─lib
│  │  ├─io.netty....jar
│  │  ├─com.google....jar
│  │  ├─com.github....jar
│  │  ├─org.apache.....jar

您还可以将依赖的 jar 包中的文件复制到入口 jar 包中,如下所示:

├─executable jar
│  ├─META-INF
│  │  ├─MANIFEST.MF
│  ├─com...
│  ├─io.netty.... classes
│  ├─com.google.. classes
│  ├─com.github.. classes
│  ├─org.apache.. classes

Spring Boot 可以被看作是将可执行 jar 交付给数千个家庭。Spring Bootspring-boot-maven-plugin 插件可以在构建过程中将所有依赖的 jar 包打包成一个入口 jar 文件,并通过 Spring Boot 的类加载器和启动类来将这些依赖 jar 包加载到可执行的入口 jar 包中,上面描述的第一种方法是:将依赖的 jar 包直接放进入口 jar 包中。

1.3、Uber Jar

当我第一次看到这个词时,我不知所措。我不知道这个词是什么意思。优步出租车?查找信息后,我发现 Uber jar 的原始单词是 Über jar,是一个德语单词,可以解释为 overend,但在实际上下文中,将其翻译为 everything 可能更合适。

这个术语最初是由开发人员创造的,他们认为将所有依赖项和自己的代码放入 jar 文件可以解决许多冲突。但大多数输入法很难输入 Ü,所以被称为 Uber

1.4、Shade jar/Shadow jar

Shade jar 就是将依赖包也打包到入口 jar 包的 jar 包,并提供对某些依赖包进行重命名的功能。例如,一个 Maven 项目依赖于许多第三方软件包,但您希望在实际打包期间重命名一些软件包。重命名的过程在这里可以称为 Shade(着色)。

为什么要重命名依赖包呢?例如,当我们开发时还需要依赖一些第三方软件包,比如 netty,所以我们需要在实际操作中以 –javaagent 或动态附加 jar 包的形式加载我们的代理 jar 包。这里加载的代理只能是一个独立的 jar 包,因此首先,我们需要通过在 jar 包中键入我们的代理及其依赖包来构建一个 Uber jar。然后,我们需要考虑类包冲突的问题,因为代理中的依赖包类和目标 JVM 进程中的类可能会发生冲突,例如,代理依赖于 netty 4.1.58.final,而目标 JVM 进程依赖于 netty 4.0.14.final,我们的代理使用 4.0.14 中不存在的 API。此时,程序将产生找不到方法的异常,因为目标进程已加载该类,并且不会重复加载代理程序包中具有相同全限定名的类。

在构建 Uber jar 时,可以修改并重新定位依赖包的包名,这比下载项目的源代码重构包名再打包要方便的多。比如,将 io.netty 修改为 com.github.kongwu.io.netty,同时,Java 代码中的所有引用在重新定位后都使用被修改后的包名。这样,通过修改包名,完全避免了依赖性包类冲突的问题。Google 也开源了一个类似功能的 jar 文件,叫做 jarjar.jar(好多 jar 啊)。

上述 relocation 行为称为 ShadeShadowMaven 中的 Shade 插件可以将程序打包到单独的jar包中,包括依赖项包。另一个类似的 Maven Assembly 插件也可以达到同样的效果。Gradle 中也有类似的插件,功能也很强大,也支持 Shade 功能。

2、基本使用

maven-plugin-shade 必须和 Maven 构建生命周期中的 package 阶段绑定,也就是说,当执行 mvn package 时会自动触发 shade

要使用 maven-plugin-shade,只需要在 pom.xml 标签下添加它的配置即可,示例如下:


    ...
    
        
            
                org.apache.maven.plugins
                maven-shade-plugin
                3.1.0
                
                    
                
                
                    
                        
                        package
                        
                            shade
                        
                    
                
            
        
    
    ...

默认情况下会把项目所有的依赖都包含进最终的 jar 包中。当然,我们也可以在 标签内配置更具体的规则。

3、常用功能

3.1 按需选择要添加到最终 jar 包中依赖

使用 排除不需要的依赖,示例如下:


    
        
            classworlds:classworlds
            junit:junit
            jmock:*
            *:xml-apis
            org.apache.maven:lib:tests
            log4j:log4j:jar:
        
    

使用 结合 & 标签可实现更灵活的依赖选择,示例如下:

<configuration>
    <filters>
        <filter>
            <artifact>junit:junitartifact>
            <includes>
                <include>junit/framework/**include>
                <include>org/junit/**include>
            includes>
            <excludes>
                <exclude>org/junit/experimental/**exclude>
                <exclude>org/junit/runners/**exclude>
            excludes>
        filter>
        <filter>
            <artifact>*:*artifact>
            <excludes>
                <exclude>META-INF/*.SFexclude>
                <exclude>META-INF/*.DSAexclude>
                <exclude>META-INF/*.RSAexclude>
            excludes>
        filter>
    filters>
configuration>

上文中提及的

META-INF/*.SF
META-INF/*.DSA
META-INF/*.RSA

是为了解决 java.lang.SecurityException: Invalid signature file digest for Manifest main attributes 异常。

除了可以通过自定义的 filters 来过滤依赖,此插件还支持自动移除项目中没有使用到的依赖,以此来最小化 jar 包的体积,只需要添加一项配置即可。示例如下:


    true

3.2、重定位 class 文件

如果最终的 jar 包被其他的项目所依赖的话,直接地引用此 jar 包中的类可能会导致类加载冲突,这是因为 classpath 中可能存在重复的 class 文件。为了解决这个问题,我们可以使用 shade 提供的重定位功能,把部分类移动到一个全新的包中。示例如下:


    
        
            org.codehaus.plexus.util
            org.shaded.plexus.util
            
                org.codehaus.plexus.util.xml.Xpp3Dom
                org.codehaus.plexus.util.xml.pull.*
            
        
    

涉及标签:

  • :原始包名
  • :重命名后的包名
  • :原始包内不需要重定位的类,类名支持通配符

例如,在上述示例中,我们把 org.codehaus.plexus.util 包内的所有子包及 class 文件(除了 ~.xml.Xpp3Dom~.xml.pull 之外的所有 class 文件)重定位到了 org.shaded.plexus.util 包内。

当然,如果包内的大部分类我们都不需要,一个个排除就显得很繁琐了。此时我们也可以使用 标签来指定我们仅需要的类,示例如下:


    ...
    
        org.codehaus.plexus.util
        org.shaded.plexus.util
        
            org.codehaud.plexus.util.io.*
        
    
    ...

3.3、生成可执行 jar 包

使用 maven-plugin-shade 后,最终生成的 jar 包可以包含所有项目所需要的依赖。我们会想,能不能直接运行这个 uber-jar 呢?答案是当然可以,并且十分简单,只需要指定 启动类就可以了。示例如下:


    ...
    
        
            
                org.sonatype.haven.HavenCli
            
        
    
    ...

熟悉 jar 包的朋友们都知道,jar 包中默认会包含一个 MANIFEST.MF 文件,里面描述了一些 jar 包的信息。使用 java 自带的 jar 命令打包的时候可以指定 MANIFEST.MF,其中也可以指定 Main-Class 来使得 jar 包可运行。那么使用 shade 来指定和直接在 MANIFEST.MF 文件中指定有什么区别呢?

答案是没有区别,细心的读者会发现 标签的父标签是 有一个 implementation 属性,其值为 "~.ManifestResourceTransformer",意思是 Manifest 资源文件转换器。上述示例指定了启动类,因此 shade 会为我们自动生成一个包含 Main-ClassMANIFEST.MF 文件,然后在打 jar 包时指定这个文件。

那如果我们想要完全定制 MANIFEST.MF 文件内容怎么办呢?我们可以使用 标签,示例如下:


    ...
    
        
            
                
                    org.sonatype.haven.ExodusCli
                    123
                
            
        
    
    ...

你可能感兴趣的:(Maven,maven,java,jar)