IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Boot 打包一站式解决方案 -> 正文阅读

[Java知识库]Spring Boot 打包一站式解决方案

一、场景介绍

  • 众所周知 Spring Boot 应用是目前企业最主流的一套快速搭建项目的脚手架

  • 在企业应用搭建过程中,所有繁琐的 XMl 配置,都被注解取代,约定大于配置、自动装配等功能大大提高了项目框架搭建的效率

  • Spring Boot 项目部署采用 spring-boot-maven-plugin 插件打出来的 JAR 包,是可独立运行的(依赖包、业务包、服务器容器一并打成 JAR 包),这一点方便了部署和发布

  • 但是也正因为 Fat Jar 这一特征,针对不同的业务部署场景,也需要不同的部署方案:

    • 开发、测试环境,一般都是在公司的内网,通过 Jenkins 自动构建打包独立部署,无论 Fat Jar 文件有多大,上传速度还是问题不大

    • 预发、生产环境,一般都是云服务器部署,如果还是采用 Fat Jar 打包部署,由于部署包太大势必会受到带宽的影响而拉长部署周期,特别是项目业务功能微调需要快速修复的情况

  • 在实际的开发过程中,一般项目框架搭建好以后,基础依赖发生变化的可能性不是很大,所以我们在进行外网快速部署的时候,可以通过这个特征来综合考虑打包方式

  • 为适应以上场景的描述,我们将介绍两种打包方式,以备大家酌情选择

二、项目搭建

  1. 新建 Maven 父子工程

    example
        - example-common
            - CommonUtil.java
            - pom.xml
        - example-service
            - ServiceApplication.java
            - pom.xml
    - pom.xml        
    

    P.S

    • exampleexample-commonexample-service 的父工程

    • example-service 依赖 example-common 模块

    • example-service 模块包含主启动类,为标准都 spring boot 工程,example-common 模块无启动类

    • 父工程 pom

      <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      
          <!-- 模块坐标信息 GAV -->
          <groupId>com.rambo</groupId>
          <artifactId>example</artifactId>
          <packaging>pom</packaging>
          <version>V1.0.0.1</version>
      
          <name>${project.artifactId}</name>
          <description>父子模块示例工程 —— 基础父工程</description>
      
          <!-- 子模块列表 -->
          <modules>
              <module>example-common</module>
              <module>example-service</module>
          </modules>
      
          <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
              <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
              <java.version>1.8</java.version>
              <maven.compiler.source>1.8</maven.compiler.source>
              <maven.compiler.target>1.8</maven.compiler.target>
              <spring-boot.version>2.5.2</spring-boot.version>
          </properties>
          
          <dependencyManagement>
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-dependencies</artifactId>
                      <version>${spring-boot.version}</version>
                      <type>pom</type>
                      <scope>import</scope>
                  </dependency>
              </dependencies>
          </dependencyManagement>
      
          <build>
              <finalName>${project.artifactId}</finalName>
          </build>
      </project>
      
    • example-common 模块 pom

      <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      
          <!-- 父工程 GAV -->
          <parent>
              <artifactId>example</artifactId>
              <groupId>com.rambo</groupId>
              <version>V1.0.0.1</version>
          </parent>
      
          <!-- 本工程模块 AV -->
          <artifactId>example-common</artifactId>
          <version>V1.0.0.1</version>
          <packaging>jar</packaging>
      
          <name>${project.artifactId}</name>
          <description>无启动类的示例通用模块 —— 通用工具</description>
          
          <dependencies>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
              </dependency>
          </dependencies>
      </project>
      
    • example-service 模块 pom

      <?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>
      
          <!-- 父工程 GAV -->
          <parent>
              <artifactId>example</artifactId>
              <groupId>com.rambo</groupId>
             <version>V1.0.0.1</version>
          </parent>
      
          <!-- 本工程模块 AV -->
          <artifactId>example-service</artifactId>
          <version>V1.0.0.1</version>
          <packaging>jar</packaging>
      
          <name>example-service</name>
          <description>有启动类的示例服务模块 —— 示例服务</description>
      
          <dependencies>
              <!-- Spring Boot Web -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
      
              <!-- Spring Boot Test -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
              </dependency>
              
              <!-- 示例通用工具模块 -->
              <dependency>
                  <groupId>com.rambo</groupId>
                  <artifactId>example-common</artifactId>
                  <version>V1.0.0.1</version>
              </dependency>
              
              <!-- 方便解读编译后的字节码 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-loader</artifactId>
                  <scope>provided</scope>
              </dependency>
          </dependencies>
      </project>
      
  2. example-commonexample-service 的示例代码如下

    • example-common 模块

      public class CommonUtil {
          public static void info() {
              System.out.println("This is info from CommonUtil.class");
          }
      }
      
    • example-service 模块

      @SpringBootApplication
      public class ServiceApplication {
          public static void main(String[] args) {
              SpringApplication.run(ServiceApplication.class, args);
              CommonUtil.info();
          }
      }
      

二、打包方案一(Fat Jar)

  1. example-service 模块的 pom 文件中添加 spring-boot-maven-plugin 的打包插件

    <build>
        <plugins>
            <!-- Fat Jar 打包 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    
  2. 打包完成以后,解压 Fat Jar 进行解析

    • example-service 模块的 MANIFEST.MF 文件内容如下

      Manifest-Version: 1.0
      Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
      Archiver-Version: Plexus Archiver
      Built-By: rambo
      Spring-Boot-Layers-Index: BOOT-INF/layers.idx
      Start-Class: com.rambo.service.ServiceApplication
      Spring-Boot-Classes: BOOT-INF/classes/
      Spring-Boot-Lib: BOOT-INF/lib/
      Spring-Boot-Version: 2.5.2
      Created-By: Apache Maven 3.6.3
      Build-Jdk: 1.8.0_271
      Main-Class: org.springframework.boot.loader.JarLauncher
      
    • BOOT-INFO/class 目录存放应用编译后的 class 文件

    • BOOT-INFO/lib 目录存放应用依赖的 JAR 包,包括 Tomcat 容器

    • META-INF/ 目录存放应用相关的元信息,如:MANIFEST.MF

    • org/ 目录存放 Spring Boot 相关的 class 文件

    P.S

    由于 Fat Jar 是采用 org.springframework.boot.loader.JarLauncher 代理启动我们的 Start-Class 应用的,我们将 Fat Jar 解压后进入根目录采用如下命令启动也是可以的

    java org.springframework.boot.loader.JarLauncher
    

    关于 JarLauncherWarLauncher 代理启动的原理,大家可以参考 《Spring Boot 代理启动 —— JarLauncher & WarLauncher》 一文

  3. 启动测试

    ? ~/WorkSpace/example/example-service/target/ java -jar example-service.jar
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.5.2)
    
    2021-07-14 16:16:20.605  INFO 44058 --- [           main] com.rambo.service.ServiceApplication     : Starting ServiceApplication using Java 1.8.0_271 on Rambos-MacBook-Pro.local with PID 44058 (/Users/rambo/WorkSpace/example/example-service/target/example-service.jar started by rambo in /Users/rambo/WorkSpace/example/example-service/target)
    2021-07-14 16:16:20.607  INFO 44058 --- [           main] com.rambo.service.ServiceApplication     : No active profile set, falling back to default profiles: default
    2021-07-14 16:16:21.584  INFO 44058 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 9999 (http)
    2021-07-14 16:16:21.596  INFO 44058 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2021-07-14 16:16:21.596  INFO 44058 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.48]
    2021-07-14 16:16:21.665  INFO 44058 --- [           main] o.a.c.c.C.[.[localhost].[/example]       : Initializing Spring embedded WebApplicationContext
    2021-07-14 16:16:21.665  INFO 44058 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1003 ms
    2021-07-14 16:16:22.012  INFO 44058 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9999 (http) with context path '/example'
    2021-07-14 16:16:22.022  INFO 44058 --- [           main] com.rambo.service.ServiceApplication     : Started ServiceApplication in 2.238 seconds (JVM running for 2.667)
    This is info from CommonUtil.class
    
  4. 总结

    • 一键打包后,所有的辅助依赖和业务依赖以及容器以来都大到了一个 JAR 文件中,主要将文件放置于有 JRE 环境的服务器即可采用 java -jar xxxxx.jar 直接运行

    • 独立 JAR 文件很大,如果是外网传输,会受到带宽的制约,影响发版速度。而每次实际需要更新的代码一般都是业务代码,辅助依赖基本上是不会改变的,但是没有分离出来会导致重复上传

三、打包方案二(Thin Jar)

  1. example-service 模块的 pom 文件中替换 spring-boot-maven-pluginmaven-dependency-plugin 插件,实现业务 JAR 和依赖 JAR 分开打包

    <build>
        <plugins>
            <!-- Thin Jar 打包 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <!-- 指定主启动类
                    <mainClass>com.rambo.service.ServiceApplication</mainClass>
                    -->
                    <!-- None in one jar 必须是 ZIP, All in one jar 可以是 JAR -->
                    <layout>ZIP</layout>
                    <!-- 构建完整可直接运行的执行程序 -->
                    <executable>true</executable>
                    <!-- 使用外部配置文件,jar包里没有资源文件 -->
                    <addResources>true</addResources>
                    <outputDirectory>${project.build.directory}</outputDirectory>
                    <includes>
                        <!-- 根据 POM 文件获取依赖的 JAR 包 -->
                        <!-- mvn dependency:copy-dependencies -DoutputDirectory=/path/to/lib -DincludeScope=runtime -->
                        <!-- 根据外部 lib 的依赖启动 JAR 包 -->
                        <!-- ava -jar -Dloader.path=/path/to/lib blog-web.jar >> web.log -->
                        <!-- 编译出不带 lib 文件夹的 JAR 包 -->
                        <include>
                            <groupId>nothing</groupId>
                            <artifactId>nothing</artifactId>
                        </include>
                    </includes>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- 拷贝依赖插件,也可以通过 mvn dependency:copy-dependencies -DoutputDirectory=/path/to/lib -DincludeScope=runtime 命令来收集依赖包 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!-- 将公共依赖导出至 lib 文件夹 -->
                            <outputDirectory>${project.build.directory}/lib/</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    
  2. 解压 Thin Jar 进行解析

    • example-service 模块的 MANIFEST.MF 文件内容如下

      Manifest-Version: 1.0
      Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
      Archiver-Version: Plexus Archiver
      Built-By: rambo
      Spring-Boot-Layers-Index: BOOT-INF/layers.idx
      Start-Class: com.rambo.service.ServiceApplication
      Spring-Boot-Classes: BOOT-INF/classes/
      Spring-Boot-Lib: BOOT-INF/lib/
      Spring-Boot-Version: 2.5.2
      Created-By: Apache Maven 3.6.3
      Build-Jdk: 1.8.0_271
      Main-Class: org.springframework.boot.loader.PropertiesLauncher
      
    • BOOT-INFO/class 目录存放应用编译后的 class 文件

    • BOOT-INFO/lib 该目录下的公共依赖都没有被打进来

    • META-INF/ 目录存放应用相关的元信息,如:MANIFEST.MF

    • org/ 目录存放 Spring Boot 相关的 class 文件

    P.S

    由于 Thin Jar 是采用 org.springframework.boot.loader.PropertiesLauncher 代理启动我们的 Start-Class 应用的,我们将 Thin Jar 解压后进入根目录采用如下命令启动也是可以的

    # ./lib 就是项目以来文件夹 lib 所在的路径
    java -jar -Dloader.path=./lib example-service.jar
    

    PropertiesLauncher 可以通过外部属性来启动

    Mark

  3. 打包后的结构,通过下图可以看出,公共的以来都保存在 lib 文件夹中,和 Thin Jar 同级

    Mark

  4. 启动测试

    ? ~/WorkSpace/example/example-service/target/ java -jar -Dloader.path=./lib example-service.jar                          
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.5.2)
    
    2021-07-14 16:50:49.247  INFO 44580 --- [           main] com.rambo.service.ServiceApplication     : Starting ServiceApplication using Java 1.8.0_271 on Rambos-MacBook-Pro.local with PID 44580 (/Users/rambo/WorkSpace/example/example-service/target/example-service.jar started by rambo in /Users/rambo/WorkSpace/example/example-service/target)
    2021-07-14 16:50:49.249  INFO 44580 --- [           main] com.rambo.service.ServiceApplication     : No active profile set, falling back to default profiles: default
    2021-07-14 16:50:50.253  INFO 44580 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 9999 (http)
    2021-07-14 16:50:50.264  INFO 44580 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2021-07-14 16:50:50.265  INFO 44580 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.48]
    2021-07-14 16:50:50.329  INFO 44580 --- [           main] o.a.c.c.C.[.[localhost].[/example]       : Initializing Spring embedded WebApplicationContext
    2021-07-14 16:50:50.329  INFO 44580 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1031 ms
    2021-07-14 16:50:50.719  INFO 44580 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9999 (http) with context path '/example'
    2021-07-14 16:50:50.729  INFO 44580 --- [           main] com.rambo.service.ServiceApplication     : Started ServiceApplication in 3.008 seconds (JVM running for 3.427)
    This is info from CommonUtil.class
    
  5. 总结

    • 通常情况下,一个工程项目架构确定以后,引入的 JAR 包基本上是不会发生改变的,改变的大部分都是业务逻辑代码

    • 后面如果需要更新业务逻辑代码,只需要清亮地编译工程,上传 Thin Jar ,可以大大提高项目部署的效率

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-15 23:43:10  更:2021-07-15 23:44:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/18 17:54:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码