Spring Boot 打包部署,JAR vs WAR

大家好,我是小悟。

一、JAR 和 WAR 打包的区别

JAR (Java ARchive)

  • 独立运行 :内嵌Tomcat/Jetty服务器,可直接通过 java -jar 运行
  • 微服务友好:适合云原生、容器化部署
  • 简化部署:单个可执行文件,无需外部Web服务器
  • 默认方式:Spring Boot 默认打包为可执行JAR

WAR (Web Application ARchive)

  • 传统部署:需要外部Servlet容器(如Tomcat、WebLogic)
  • 企业环境:适合需要部署到现有应用服务器的场景
  • 共享资源:多个应用可共享服务器资源
  • 配置分离:服务器配置与应用分离

二、详细打包部署步骤

项目结构

css 复制代码
my-springboot-app/
├── src/
│   └── main/
│       ├── java/com/example/
│       └── resources/
└── pom.xml 或 build.gradle

1. 打包为可执行JAR(默认方式)

Maven 配置 (pom.xml)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.example</groupId>
    <artifactId>my-springboot-app</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>  <!-- 打包为JAR -->
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.5</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <!-- Spring Boot Maven 插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.example.MyApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Gradle 配置 (build.gradle)

ini 复制代码
plugins {
    id 'org.springframework.boot' version '3.1.5'
    id 'io.spring.dependency-management' version '1.1.3'
    id 'java'
}

group = 'com.example'
version = '1.0.0'
sourceCompatibility = '17'

jar {
    enabled = true
    archiveClassifier = ''  // 移除plain后缀
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

// 可执行JAR配置
bootJar {
    mainClass = 'com.example.MyApplication'
    archiveClassifier = ''
}

主启动类

typescript 复制代码
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

打包命令

bash 复制代码
# Maven
mvn clean package

# Gradle
./gradlew clean build

运行JAR

ini 复制代码
# 直接运行
java -jar target/my-springboot-app-1.0.0.jar

# 指定配置文件
java -jar target/my-springboot-app-1.0.0.jar \
  --spring.config.location=classpath:/,file:./config/

# 指定端口
java -jar target/my-springboot-app-1.0.0.jar \
  --server.port=8081

# 生产环境运行(内存优化)
java -Xms512m -Xmx1024m -jar target/my-springboot-app-1.0.0.jar \
  --spring.profiles.active=prod

2. 打包为WAR(用于外部容器)

修改Maven配置

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.example</groupId>
    <artifactId>my-springboot-app</artifactId>
    <version>1.0.0</version>
    <packaging>war</packaging>  <!-- 修改为WAR -->
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.5</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 排除内嵌Tomcat -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <!-- 添加Servlet API依赖(provided scope) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>  <!-- 由外部容器提供 -->
        </dependency>
    </dependencies>
    
    <build>
        <finalName>myapp</finalName>  <!-- WAR文件名称 -->
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

修改启动类

scala 复制代码
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(MyApplication.class);
    }
    
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

Gradle WAR配置

bash 复制代码
plugins {
    id 'org.springframework.boot' version '3.1.5'
    id 'io.spring.dependency-management' version '1.1.3'
    id 'java'
    id 'war'  // 添加war插件
}

group = 'com.example'
version = '1.0.0'
sourceCompatibility = '17'

configurations {
    providedRuntime  // 用于provided范围的依赖
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}

war {
    archiveFileName = 'myapp.war'
}

打包WAR

bash 复制代码
# Maven
mvn clean package

# Gradle
./gradlew clean build

部署到外部Tomcat

准备Tomcat服务器

bash 复制代码
# 下载Tomcat
wget https://downloads.apache.org/tomcat/tomcat-10/v10.1.13/bin/apache-tomcat-10.1.13.tar.gz
tar -xzf apache-tomcat-10.1.13.tar.gz
cd apache-tomcat-10.1.13

部署WAR文件

bash 复制代码
# 复制WAR文件到webapps目录
cp target/myapp.war /opt/tomcat/webapps/

# 或者使用管理界面部署
# 访问 http://localhost:8080/manager/html

Tomcat配置优化(conf/server.xml)

ini 复制代码
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           maxThreads="200"
           minSpareThreads="10"
           enableLookups="false"
           acceptCount="100"
           disableUploadTimeout="true"/>

启动/停止Tomcat

python 复制代码
# Linux
./bin/startup.sh
./bin/shutdown.sh

# Windows
bin\startup.bat
bin\shutdown.bat

3. 配置文件管理

application.yml 示例

yaml 复制代码
# 公共配置
spring:
  application:
    name: my-springboot-app

# 开发环境
---
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 8080
logging:
  level:
    root: DEBUG

# 生产环境
---
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 8080
  compression:
    enabled: true
    mime-types: text/html,text/xml,text/plain,application/json
logging:
  file:
    name: /var/log/myapp.log
  level:
    root: INFO

4. 容器化部署(Docker)

Dockerfile for JAR

bash 复制代码
# 使用多阶段构建
FROM eclipse-temurin:17-jdk-alpine as builder
WORKDIR /app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
RUN ./mvnw dependency:go-offline
COPY src ./src
RUN ./mvnw clean package -DskipTests

# 运行阶段
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Dockerfile for WAR + Tomcat

bash 复制代码
FROM tomcat:10.1-jdk17-temurin
# 移除默认应用
RUN rm -rf /usr/local/tomcat/webapps/*
# 复制WAR文件
COPY target/myapp.war /usr/local/tomcat/webapps/ROOT.war
# 暴露端口
EXPOSE 8080
# 启动Tomcat
CMD ["catalina.sh", "run"]

Docker Compose 示例

yaml 复制代码
version: '3.8'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DB_HOST=mysql
    depends_on:
      - mysql
    networks:
      - app-network

  mysql:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
      - MYSQL_DATABASE=mydb
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  mysql-data:

三、部署脚本示例

Linux 服务脚本(systemd)

ini 复制代码
# /etc/systemd/system/myapp.service
[Unit]
Description=My Spring Boot Application
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -Xms512m -Xmx1024m -jar myapp.jar
SuccessExitStatus=143
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

部署脚本

bash 复制代码
#!/bin/bash
# deploy.sh

APP_NAME="my-springboot-app"
JAR_PATH="target/$APP_NAME-1.0.0.jar"
DEPLOY_DIR="/opt/$APP_NAME"
BACKUP_DIR="$DEPLOY_DIR/backup"
LOG_DIR="/var/log/$APP_NAME"

# 创建目录
mkdir -p $DEPLOY_DIR $BACKUP_DIR $LOG_DIR

# 备份旧版本
if [ -f "$DEPLOY_DIR/$APP_NAME.jar" ]; then
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    mv "$DEPLOY_DIR/$APP_NAME.jar" "$BACKUP_DIR/$APP_NAME-$TIMESTAMP.jar"
fi

# 复制新版本
cp $JAR_PATH "$DEPLOY_DIR/$APP_NAME.jar"

# 设置权限
chmod 755 "$DEPLOY_DIR/$APP_NAME.jar"

# 重启服务
systemctl restart $APP_NAME

# 检查状态
sleep 10
systemctl status $APP_NAME

四、总结对比

特性 JAR 部署 WAR 部署
部署方式 独立运行,内嵌容器 依赖外部Servlet容器
启动命令 java -jar app.jar 容器启动(如Tomcat)
适合场景 微服务、云原生、容器化 传统企业环境、共享服务器
依赖管理 包含所有依赖(fat jar) 部分依赖由容器提供
资源占用 每个应用独立容器 多个应用共享容器资源
热部署 需要重启整个应用 Tomcat支持部分热部署
配置管理 应用自身管理 可与容器配置结合
监控 Spring Boot Actuator 容器管理界面 + Actuator

选择建议:

  1. 选择JAR的情况
    • 微服务架构
    • 容器化部署(Docker/K8s)
    • 云原生应用
    • 需要快速独立部署
    • 无现有应用服务器
  2. 选择WAR的情况
    • 传统企业IT环境
    • 已有Tomcat/WebLogic集群
    • 需要与其他Java EE应用共享资源
    • 运维团队熟悉传统部署模式
    • 需要与旧系统集成

最佳实践:

  1. 对于新项目,优先考虑JAR部署,更符合现代架构
  2. 统一配置管理:无论JAR/WAR,使用外部配置文件
  3. 健康检查:集成Spring Boot Actuator用于监控
  4. 日志集中:使用Logback/SLF4J,日志输出到文件或日志系统
  5. 安全加固:生产环境关闭开发特性,使用HTTPS
  6. 自动化部署:结合CI/CD流水线实现自动化

通过合理的打包部署策略,可以确保Spring Boot应用在不同环境中的稳定运行和高效维护。

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

相关推荐
IT_陈寒11 小时前
React 18实战:这5个新特性让我的开发效率提升了40%
前端·人工智能·后端
毕设源码-朱学姐12 小时前
【开题答辩全过程】以 日程管理系统为例,包含答辩的问题和答案
java
a努力。12 小时前
京东Java面试被问:双亲委派模型被破坏的场景和原理
java·开发语言·后端·python·面试·linq
小毛驴85012 小时前
Maven同时配置阿里云仓库和私有仓库
java·阿里云·maven
刘975312 小时前
【第25天】25c#今日小结
java·开发语言·c#
不如打代码KK12 小时前
Springboot如何解决跨域问题?
java·spring boot·后端
豆沙沙包?12 小时前
2026年--Lc330-394. 字符串解码(栈)--java版
java·开发语言
蓝程序12 小时前
Spring AI学习 程序接入大模型
java·人工智能·spring
nice_lcj52012 小时前
数据结构之树与二叉树:重点梳理与拓展
java·数据结构
毕设源码-钟学长12 小时前
【开题答辩全过程】以 助学贷款管理系统为例,包含答辩的问题和答案
java