背景

本篇文章记录下如何使用Maven插件将核心代码包和依赖包分离的方法,最近在公司业务代码的加密工作,使用加密工具针对SpringBoot工程代码进行加密的时候,发现各种报错。无法进行加密,询问了开发加密客户端的同学,反馈说应用程序依赖包太多,导致打包失败,要求去除所依赖的包,只对“核心代码”进行加密,所以就有了本篇文章。

这里分两种方案进行实现,其实就是基于Maven插件的配置实现

  1. Maven打包后出现两个jar文件,一个是包含核心代码的jar包,一个是包含依赖包以及核心代码的jar包。
  2. Maven打包后只有一个jar文件,该jar文件只包含核心代码,应用所有依赖包都将导出到本地lib目录下。

我们先看下演示项目目录,如下图:

可以看到,这是一个很简单的SpirngBoot工程

第一种方案

先来演示配置maven插件最终打包生成两个jar文件,maven配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.mengxi</groupId>
    <artifactId>maven-plugin-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>maven-plugin-test</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <version>3.3</version>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <encoding>UTF-8</encoding><!-- 指定编码格式,否则在DOS下运行mvn命令时当发生文件资源copy时将使用系统默认使用GBK编码 -->
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

通过上述pom可以看出,这里的dependency只有简单的spring-boot-starter-web、mysql-connector-java、lombok、spring-boot-starter-test 四个依赖,通过上述pom打包完成会产生两个Jar文件,如下图

maven-plugin-test-0.0.1-SNAPSHOT.jar —> 核心代码文件,我们可以通过反编译工具具体查看下这个文件,如果需要下载反编译工具可参考反编译工具下载

可以看出来,这个jar文件并没有依赖包

接下来,我们再来看maven-plugin-test-0.0.1-SNAPSHOT-jar-with-dependencies.jar,如下图,红圈圈住的地方就是引用的依赖包。

使用上述pom文件,打包可以实现核心代码与依赖文件分离。

第二种方案

这种方案其实就是打一个核心代码的jar,然后把该jar所依赖的文件,通过maven插件落地到本地文件夹中,然后通过修改META-INF.MANIFEST.MF文件,在访问的时候可以通过Class-Path去加载依赖包。

老套路,先上pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.mengxi</groupId>
    <artifactId>maven-plugin-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>maven-plugin-test</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
            <scope>system</scope>
            <systemPath>${basedir}/src/main/resources/lib/mysql-connector-java-5.1.46.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 指定启动类,将依赖打成外部jar包 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <!-- 添加classpath缺少的内容-->
                        <manifestEntries>
                            <Class-Path>lib/mysql-connector-java-5.1.46.jar
                            </Class-Path>
                        </manifestEntries>
                        <!-- 生成的jar中,不要包含pom.xml和pom.properties这两个文件 -->
                        <addMavenDescriptor>false</addMavenDescriptor>
                        <manifest>
                            <!-- 是否要把第三方jar加入到类构建路径 -->
                            <addClasspath>true</addClasspath>
                            <!-- 外部依赖jar包的最终位置 -->
                            <classpathPrefix>lib/</classpathPrefix>
                            <!-- 项目启动类 -->
                            <mainClass>com.mengxi.maven.plugin.test.MavenPluginTestApplication</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <!--拷贝依赖到jar外面的lib目录-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-lib</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/lib</outputDirectory>
                            <excludeTransitive>false</excludeTransitive>
                            <stripVersion>false</stripVersion>
                            <includeScope>compile</includeScope>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

通过pom文件,看到利用maven-jar-plugin插件,将依赖包导出到target/lib目录中,这里需要指定下主方法,最终输出的jar文件是没有依赖的,可以将lib目录上传到服务器上,与当前jar文件同级目录即可,使用java -jar maven-plugin-test-0.0.1-SHAPSHOT.jar 运行。

使用如下命令进行打包

mvn clean package -Dmaven.test.skip=true

如上图,最终将依赖包输出到lib目录下,并且输出无依赖的jar文件 maven-plugin-test-0.0.1-SNAPSHOT.jar

再来使用反编译工具验证一下 maven-plugin-test-0.0.1-SNAPSHOT.jar 文件

如上图,已经标记的很清楚了,最终把没有依赖的jar文件和lib文件夹丢到服务器上,直接运行就可以了,如果下次在没有修改依赖文件的情况下,我们只需要更新核心代码的jar文件就可以了,这样jar文件也变得很轻量级。