maven学习篇之maven-shade-plugin

maven-shade-plugin简介

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-shade-plugin的作用,那就是作为一个超级jar包(uber-jar),在基本的管理依赖方面,需要支持更多的功能,如:
1,将依赖的jar包打包时传入到当前classes目录(默认在lib文件下)。
2,将依赖的jar包重命名,比如你在当前项目中依赖了tomcat-9.0的jar包,但是需求是需要项目在tomcat-8.5中允许,在出现版本冲突的时候,你可以使用maven-shade-plugin将你依赖的tomcat-9.0的jar包重命名并添加至你的classes目录下。

maven-shade-plugin使用

  • 对jar包中的依赖进行更详细的筛选
  1. 对jar包进行添加或排除
    <project>
     ...
       <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.1</version>
            <executions>
              <execution>
                <phase>package</phase>
                <goals>
                  <goal>shade</goal>
                </goals>
                <configuration>
                  <artifactSet>
                    <excludes>
                      <exclude>classworlds:classworlds</exclude>
                      <exclude>junit:junit</exclude>
                      <exclude>jmock:*</exclude>
                      <exclude>*:xml-apis</exclude>
                      <exclude>org.apache.maven:lib:tests</exclude>
                      <exclude>log4j:log4j:jar:</exclude>
                    </excludes>
                  </artifactSet>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
      ...
    </project>
    
  2. 对jar包里面的类进行添加或排除
    <project>
          ...
    	  <build>
    	    <plugins>
    	      <plugin>
    	        <groupId>org.apache.maven.plugins</groupId>
    	        <artifactId>maven-shade-plugin</artifactId>
    	        <version>3.2.1</version>
    	        <executions>
    	          <execution>
    	            <phase>package</phase>
    	            <goals>
    	              <goal>shade</goal>
    	            </goals>
    	            <configuration>
    	              <filters>
    	                <filter>
    	                  <artifact>junit:junit</artifact>
    	                  <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/*.SF</exclude>
    	                    <exclude>META-INF/*.DSA</exclude>
    	                    <exclude>META-INF/*.RSA</exclude>
    	                  </excludes>
    	                </filter>
    	              </filters>
    	            </configuration>
    	          </execution>
    	        </executions>
    	      </plugin>
    	    </plugins>
    	  </build>
    	  ...
    </project>
    
  3. 自动移除没有使用的依赖
    <project>
          ...
    	  <build>
    	    <plugins>
    	      <plugin>
    	        <groupId>org.apache.maven.plugins</groupId>
    	        <artifactId>maven-shade-plugin</artifactId>
    	        <version>3.2.1</version>
    	        <executions>
    	          <execution>
    	            <phase>package</phase>
    	            <goals>
    	              <goal>shade</goal>
    	            </goals>
    	            <configuration>
    	              <minimizeJar>true</minimizeJar>
    	            </configuration>
    	          </execution>
    	        </executions>
    	      </plugin>
    	    </plugins>
    	  </build>
    	  ...
    </project>
    
  4. 组合使用
   <project>
          ...
		  <build>
		    <plugins>
		      <plugin>
		        <groupId>org.apache.maven.plugins</groupId>
		        <artifactId>maven-shade-plugin</artifactId>
		        <version>3.2.1</version>
		        <executions>
		          <execution>
		            <phase>package</phase>
		            <goals>
		              <goal>shade</goal>
		            </goals>
		            <configuration>
		              <minimizeJar>true</minimizeJar>
		              <filters>
		                <filter>
		                   <artifact>log4j:log4j</artifact>
		                   <includes>
		                       <include>**</include>
		                   </includes>
		                </filter>
		                <filter>
		                   <artifact>commons-logging:commons-logging</artifact>
		                   <includes>
		                       <include>**</include>
		                   </includes>
		                </filter>
		              </filters>            
		            </configuration>
		          </execution>
		        </executions>
		      </plugin>
		    </plugins>
		  </build>
		  ...
   </project>
  • 设置编译后的classes目录位置
    如果需要将某个jar用作其他项目的依赖项,但是又因为其依赖项中直接包含类,因此会由于类路径上的重复类而导致类加载冲突。 为了解决这个问题,可以重新定位shade组件中包含的类,以创建其字节码的私有副本:
<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <relocations>
                <relocation>
                  <pattern>org.codehaus.plexus.util</pattern>
                  <shadedPattern>org.shaded.plexus.util</shadedPattern>
                  <excludes>
                    <exclude>org.codehaus.plexus.util.xml.Xpp3Dom</exclude>
                    <exclude>org.codehaus.plexus.util.xml.pull.*</exclude>
                  </excludes>
                </relocation>
              </relocations>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

当然,我们也可以进一步的缩小其作用范围:

<project>
  ...
                <relocation>
                  <pattern>org.codehaus.plexus.util</pattern>
                  <shadedPattern>org.shaded.plexus.util</shadedPattern>
                  <includes>
                    <include>org.codehaud.plexus.util.io.*</include>
                  </includes>
                </relocation>
  ...
</project>
  • 修改打包后默认的shade文件的命名

默认情况下,shade插件会覆盖基于项目的jar包,而生成包含所有依赖的jar包。但有时需要原始的jar包和shade后的jar包同时被部署,可以配置如下:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <shadedArtifactAttached>true</shadedArtifactAttached>
              <shadedClassifierName>jackofall</shadedClassifierName> <!-- 名称会作为后缀在shade构件jar包后 -->
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
  • 执行jar包

想要生成一个可以执行的jar包,就需要指定当前应用程序下main类的位置:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>org.sonatype.haven.HavenCli</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

此代码段通过配置特殊的资源转换器在带shade插件的的JAR中的 MANIFEST.MF 中设置Main-Class条目。 其他条目也可以通过 manifestEntries 部分中的键值对添加到MANIFEST.MF

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <manifestEntries>
                    <Main-Class>org.sonatype.haven.ExodusCli</Main-Class>
                    <Build-Number>123</Build-Number>
                  </manifestEntries>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
  • 常见的资源转换器
ManifestResourceTransformer

ManifestResourceTransformer允许替换MANIFEST.MF中的现有条目并添加新条目:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <manifestEntries>
                    <Main-Class>${app.main.class}</Main-Class>
                    <X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK>
                    <X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK>
                  </manifestEntries>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
IncludeResourceTransformer

IncludeResourceTransformer允许以给定名称将项目文件包含在包中。
例如,以下示例在包中将README.txt作为META-INF目录中的README包含在内:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
                    <resource>META-INF/README</resource>
                    <file>README.txt</file>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
IncludeResourceTransformer

DontIncludeResourceTransformer允许以指定给定值结尾的方式排除资源。
例如,以下示例排除了所有以.txt结尾的资源。

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
                    <resource>.txt</resource>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
AppendingTransformer

某些jar包含具有相同文件名的其他资源(例如属性文件)。 为避免覆盖,您可以选择通过将它们的内容附加到一个文件中来合并它们。 一个很好的例子是同时聚合spring-context和plexus-spring jars。 它们都具有META-INF / spring.handlers文件,Spring使用该文件来处理XML模式名称空间。 您可以使用AppendingTransformer合并具有该特定名称的所有文件的内容,如下所示:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                  <resource>META-INF/spring.handlers</resource>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                  <resource>META-INF/spring.schemas</resource>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

对于xml文件,您可以使用XmlAppendingTransformer:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
                  <resource>META-INF/magic.xml</resource>
                  <!-- Add this to enable loading of DTDs
                  <ignoreDtd>false</ignoreDtd>
                  -->
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>
PluginXmlResourceTransformer

提供某些接口的实现的JAR文件通常带有META-INF/services/目录,该目录将接口映射到其实现类以供服务定位器查找。 要重新定位这些实现类的类名,并将同一接口的多个实现合并到一个服务条目中,可以使用ServicesResourceTransformer:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

maven-shade-plugin原理介绍

原理是利用了ASM框架来修改字节码:

static class RelocatorRemapper
        extends Remapper
    {

        private final Pattern classPattern = Pattern.compile( "(\\[*)?L(.+);" );

        List<Relocator> relocators;

        RelocatorRemapper( List<Relocator> relocators )
        {
            this.relocators = relocators;
        }

        public boolean hasRelocators()
        {
            return !relocators.isEmpty();
        }

        public Object mapValue( Object object )
        {
            if ( object instanceof String )
            {
                String name = (String) object;
                String value = name;

                String prefix = "";
                String suffix = "";

                Matcher m = classPattern.matcher( name );
                if ( m.matches() )
                {
                    prefix = m.group( 1 ) + "L";
                    suffix = ";";
                    name = m.group( 2 );
                }

                for ( Relocator r : relocators )
                {
                    if ( r.canRelocateClass( name ) )
                    {
                        value = prefix + r.relocateClass( name ) + suffix;
                        break;
                    }
                    else if ( r.canRelocatePath( name ) )
                    {
                        value = prefix + r.relocatePath( name ) + suffix;
                        break;
                    }
                }

                return value;
            }

            return super.mapValue( object );
        }

        public String map( String name )
        {
            String value = name;

            String prefix = "";
            String suffix = "";

            Matcher m = classPattern.matcher( name );
            if ( m.matches() )
            {
                prefix = m.group( 1 ) + "L";
                suffix = ";";
                name = m.group( 2 );
            }

            for ( Relocator r : relocators )
            {
                if ( r.canRelocatePath( name ) )
                {
                    value = prefix + r.relocatePath( name ) + suffix;
                    break;
                }
            }

            return value;
        }

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值