
很多Java开发者都有过这样的经历:改完一行代码,手动打包、上传服务器、修改配置、重启服务,中途遇到环境不一致、依赖缺失、配置写错,折腾半天还回滚不了;团队协作时,多人提交代码导致合并冲突,上线前集中合并引发大量bug,发布日全员加班到深夜。而CI/CD持续交付体系,就是解决这些痛点的核心方案,它能让代码从提交到发布的全流程自动化,大幅降低人为失误,提升研发交付效率与系统稳定性。
很多开发者对CI/CD的认知停留在“自动化部署工具”的层面,甚至混淆了核心概念,导致落地时出现流程设计错误。本节将明确区分易混淆的核心概念,讲透每个环节的底层逻辑。
持续集成的核心定义是:开发者频繁地将代码合并到主干分支,每次合并都通过自动化的构建、测试来验证,尽早发现集成错误。 它的底层逻辑是把“上线前集中合并代码”变成“每天多次小批量合并”,将集成风险分散到日常开发中,避免“合并地狱”。核心目标是提前验证,每次代码提交都做全量校验,不让问题代码进入主干分支。
持续交付的核心定义是:在持续集成的基础上,将经过验证的代码自动部署到预生产环境,确保代码随时处于可发布的状态,最终的发布决策由人工触发。 它的底层逻辑是打通从构建到部署的全流程自动化,消除发布前的人工操作环节,保证任何时候都有一个可随时发布的、经过完整验证的制品。核心目标是随时可发布,把发布变成一个低风险、一键式的操作。
持续部署的核心定义是:在持续交付的基础上,将通过所有验证环节的代码,自动部署到生产环境,全程无需人工干预。 它的底层逻辑是只有当自动化测试、安全扫描、性能压测等所有环节都100%通过,代码才会自动进入生产环境,把发布变成开发流程的自然延伸。核心目标是全自动发布,适合迭代速度快、自动化验证体系完善的团队。
❝核心区分点:持续交付的终点是“人工触发发布”,持续部署的终点是“自动发布到生产环境”,这是两者最核心的边界,也是行业内最容易混淆的知识点。
CI/CD不是一个单一的工具,而是一套完整的闭环交付体系,全链路的每个环节都有明确的校验规则,前一步不通过,后续环节不会执行,确保只有符合质量标准的代码才能进入下一个环节。

持续集成是整个CI/CD体系的基础,核心是代码提交后的自动化验证与构建,确保每一次代码变更都符合质量标准,不会引入新的bug和安全漏洞。
流水线的触发是CI的起点,核心是事件驱动,而非定时执行,常见的触发方式有4种,分别对应不同的业务场景:
静态代码检查的底层逻辑是:在代码运行前,通过静态分析工具检查代码规范、潜在bug、安全漏洞、性能问题,不需要执行代码,就能发现80%以上的常见编码问题,是保障代码质量的第一道门禁。
Java生态中主流的静态检查工具包括CheckStyle(代码规范检查)、SpotBugs(字节码缺陷检查)、SonarQube(全维度代码质量分析),以下是Spring Boot项目的完整配置示例。
在项目的pom.xml中添加以下插件配置,所有插件均采用当前最新稳定版本:
<build>
<plugins>
<!-- CheckStyle 代码规范检查 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<encoding>UTF-8</encoding>
<failsOnError>true</failsOnError>
<linkXRef>false</linkXRef>
</configuration>
<executions>
<execution>
<id>checkstyle</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- SpotBugs 字节码缺陷检查 -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.6.0</version>
<configuration>
<failOnError>true</failOnError>
<includeTests>true</includeTests>
</configuration>
<executions>
<execution>
<id>spotbugs</id>
<phase>test</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- SonarQube 代码质量分析 -->
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>4.0.0.4121</version>
</plugin>
<!-- Spring Boot 打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.4.3</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- OWASP 依赖安全扫描 -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>10.0.4</version>
<configuration>
<failOnCVSSScore>7.0</failOnCVSSScore>
<skipProvidedScope>true</skipProvidedScope>
<skipRuntimeScope>false</skipRuntimeScope>
</configuration>
<executions>
<execution>
<id>dependency-check</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
在项目根目录创建checkstyle.xml文件,配置基础的代码规范规则,示例如下:
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="fileExtensions" value="java"/>
<module name="TreeWalker">
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<module name="AvoidStarImport"/>
<module name="UnusedImports"/>
<module name="LineLength">
<property name="max" value="120"/>
</module>
<module name="MethodLength">
<property name="max" value="100"/>
</module>
</module>
</module>
自动化测试是CI环节的核心校验环节,核心目标是确保代码修改不会破坏原有功能。CI环节的测试设计核心是快速反馈,优先执行速度快的单元测试,再执行轻量的集成测试,避免流水线执行时间过长,影响开发效率。
Java生态主流的测试框架是JUnit 5、Mockito、TestContainers,以下是Spring Boot项目的单元测试示例。
package com.example.cicd.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello CI/CD!";
}
}
package com.example.cicd.controller;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
importstatic org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
importstatic org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
importstatic org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class HelloControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
@DisplayName("测试hello接口返回正确内容")
void shouldReturnHelloMessage() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("Hello CI/CD!"));
}
}
第三方依赖是Java项目安全漏洞的重灾区,超过70%的应用安全漏洞都是通过第三方依赖引入的。CI环节的依赖安全扫描,核心是在构建阶段就发现依赖中的高危漏洞,避免带有安全风险的代码进入生产环境。
上文中的pom.xml已经配置了OWASP Dependency-Check插件,该插件会自动扫描项目所有依赖,匹配国家漏洞数据库(NVD)中的漏洞信息,当发现CVSS分数大于等于7.0的高危漏洞时,会直接终止构建,强制开发者修复漏洞。
构建环节的核心逻辑是生成不可变的制品,制品一旦生成,内容就不会改变,所有环境都使用同一个制品,避免不同环境重新构建导致的不一致问题。Java项目的标准制品是可执行Jar包,后续的容器镜像也会基于这个Jar包构建。
标准的Maven构建命令为mvn clean package,该命令会按顺序执行clean、validate、compile、test、package等所有生命周期阶段,前文配置的代码检查、测试、安全扫描都会在这个过程中自动执行,任何一个环节失败,构建都会立即终止,确保只有符合质量标准的代码才能生成制品。
本节以GitHub Actions为例,实现完整的CI流水线,GitHub Actions与GitHub仓库深度集成,配置简单、免费开源,是目前最主流的CI/CD工具之一。
在项目仓库的.github/workflows/目录下创建ci.yml文件,配置如下:
name: JavaCIPipeline
on:
push:
branches:[main,develop]
pull_request:
branches:[main]
jobs:
build-and-verify:
runs-on:ubuntu-latest
steps:
-name:Checkoutcode
uses:actions/checkout@v4
-name:SetupJDK21
uses:actions/setup-java@v4
with:
java-version:'21'
distribution:'temurin'
cache:maven
-name:Codestylecheck
run:mvncheckstyle:check
-name:Rununittests
run:mvntest
-name:Dependencysecurityscan
run:mvnorg.owasp:dependency-check-maven:check
-name:Buildapplicationpackage
run:mvncleanpackage-DskipTests
-name:SonarQubeanalysis
env:
SONAR_TOKEN:${{secrets.SONAR_TOKEN}}
SONAR_HOST_URL:${{secrets.SONAR_HOST_URL}}
run:mvnsonar:sonar-Dsonar.projectKey=cicd-demo-Dsonar.host.url=${SONAR_HOST_URL}-Dsonar.login=${SONAR_TOKEN}
-name:Uploadbuildartifact
uses:actions/upload-artifact@v4
with:
name:app-jar
path:target/*.jar
该流水线的触发规则为:当代码推送到main、develop分支,或者提交PR到main分支时,自动触发执行。流水线按顺序执行代码拉取、JDK环境初始化、代码规范检查、单元测试、依赖安全扫描、Jar包构建、SonarQube质量分析、制品上传,所有步骤都通过后,才算CI环节完成。
持续交付是连接CI与生产发布的核心环节,核心目标是将CI环节生成的制品,自动部署到非生产环境,完成集成测试、验收测试,确保制品随时可以安全地发布到生产环境。
制品是CI的输出,也是CD的输入,制品管理的两个核心原则是不可变性和版本唯一性。
Java项目的制品管理分为两个层级:基础的Jar包管理通常采用Nexus等Maven私服;容器化部署的场景下,通常采用Docker镜像作为最终制品,通过Docker Registry进行管理,主流的Registry包括GitHub Container Registry、Harbor、Docker Hub等。
Docker容器化技术能将应用和运行环境一起打包,彻底解决“我本地能跑,线上跑不了”的环境不一致问题,是目前持续交付体系中最主流的制品形式。
在项目根目录创建Dockerfile,采用最新的JDK21 JRE Alpine基础镜像,体积小、安全性高:
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
在之前的ci.yml文件中,新增镜像构建与推送的job,实现CI环节完成后,自动构建Docker镜像并推送到Registry:
build-and-push-image:
needs:build-and-verify
runs-on:ubuntu-latest
if:github.event_name=='push'&&github.ref=='refs/heads/main'
steps:
-name:Checkoutcode
uses:actions/checkout@v4
-name:Downloadbuildartifact
uses:actions/download-artifact@v4
with:
name:app-jar
path:target
-name:SetupDockerBuildx
uses:docker/setup-buildx-action@v3
-name:LogintoGitHubContainerRegistry
uses:docker/login-action@v3
with:
registry:ghcr.io
username:${{github.actor}}
password:${{secrets.GITHUB_TOKEN}}
-name:ExtractmetadataforDocker
id:meta
uses:docker/metadata-action@v5
with:
images:ghcr.io/${{github.repository}}/cicd-demo
tags:|
type=sha,format=short
type=raw,value=latest
-name:BuildandpushDockerimage
uses:docker/build-push-action@v5
with:
context:.
push:true
tags:${{steps.meta.outputs.tags}}
cache-from:type=gha
cache-to:type=gha,mode=max
该job的needs字段确保只有前序的build-and-verify job成功后,才会执行镜像构建;if条件确保只有当代码推送到main分支时,才会构建镜像,避免开发分支的临时提交生成无效镜像。镜像同时打上了Git短哈希标签和latest标签,确保每个镜像都有唯一的版本号,便于追溯和回滚。
持续交付的核心是多环境部署,通常企业级项目会分为4个环境,每个环境有明确的用途和准入规则:
多环境部署的核心原则是:制品相同,配置不同。所有环境使用同一个Docker镜像,环境差异化的配置通过环境变量、配置中心注入,确保测试过的制品和生产环境运行的制品完全一致。
以下示例通过GitHub Actions实现镜像构建完成后,自动部署到测试服务器,采用Docker Compose进行容器编排。
首先在测试服务器上创建项目目录,编写docker-compose.yml配置文件:
version: '3.8'
services:
cicd-demo-app:
image:ghcr.io/your-username/cicd-demo:latest
container_name:cicd-demo-app
ports:
-"8080:8080"
environment:
-SPRING_PROFILES_ACTIVE=test
restart:always
healthcheck:
test:["CMD","wget","--no-verbose","--tries=1","--spider","http://localhost:8080/actuator/health"]
interval:30s
timeout:10s
retries:3
start_period:30s
为了支持健康检查,需要在项目的pom.xml中添加Spring Boot Actuator依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
同时在application.yml中配置健康检查端点:
management:
endpoints:
web:
exposure:
include: health
endpoint:
health:
show-details: never
最后在ci.yml中新增自动部署的job:
deploy-to-test:
needs:build-and-push-image
runs-on:ubuntu-latest
steps:
-name:DeploytotestserverviaSSH
uses:appleboy/ssh-action@v1.2.0
with:
host:${{secrets.TEST_SERVER_HOST}}
username:${{secrets.TEST_SERVER_USER}}
key:${{secrets.TEST_SERVER_SSH_KEY}}
script: |
cd /opt/cicd-demo
docker compose pull
docker compose up -d
docker system prune -af
该job会在镜像推送完成后,通过SSH登录到测试服务器,拉取最新的镜像,重启容器,清理无用的镜像资源,实现测试环境的自动更新,确保测试环境始终运行main分支的最新代码。
生产环境发布是整个CI/CD体系的最终环节,核心目标是低风险、零停机、可快速回滚,避免发布导致的服务不可用,影响用户体验。本节将详细讲解主流的发布策略,区分易混淆的发布模式,并提供可落地的零停机发布实战示例。
重建发布的原理是:停止旧版本服务,部署新版本服务,启动新版本服务。
滚动发布的原理是:逐个或分批替换旧版本的服务实例,直到所有实例都升级为新版本,发布过程中服务始终有实例在运行,不会停机。

蓝绿部署的原理是:准备两套完全相同的服务环境,蓝环境运行当前的线上稳定版本,绿环境部署新版本,对绿环境完成完整的测试验证后,将流量一次性从蓝环境切换到绿环境,完成发布。

金丝雀发布的原理是:先将一小部分流量切换到新版本,监控新版本的运行状态、错误率、性能指标,确认没有问题后,逐步扩大流量比例,直到所有流量都切换到新版本,期间如果出现问题,立即切回流量,终止发布。

❝核心区分点:蓝绿部署是一次性全量切换流量,金丝雀发布是逐步放量切换流量,这是两者最核心的区别,也是行业内最容易混淆的知识点。
以下示例通过Nginx反向代理+Shell脚本,实现Java服务的零停机蓝绿部署,脚本包含完整的健康检查、流量切换、异常回滚、资源清理逻辑。
在服务器上创建Nginx配置文件/etc/nginx/conf.d/cicd-demo.conf:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
创建blue-green-deploy.sh脚本,内容如下:
#!/bin/bash
set -e
# 环境变量定义
BLUE_CONTAINER="cicd-demo-blue"
GREEN_CONTAINER="cicd-demo-green"
IMAGE="ghcr.io/your-username/cicd-demo:latest"
PORT_BLUE=8080
PORT_GREEN=8081
NGINX_CONF="/etc/nginx/conf.d/cicd-demo.conf"
# 步骤1: 拉取最新版本镜像
docker pull $IMAGE
# 步骤2: 识别当前运行的环境
if docker ps --format '{{.Names}}' | grep -q "^${BLUE_CONTAINER}$"; then
CURRENT_CONTAINER=$BLUE_CONTAINER
CURRENT_PORT=$PORT_BLUE
NEW_CONTAINER=$GREEN_CONTAINER
NEW_PORT=$PORT_GREEN
else
CURRENT_CONTAINER=$GREEN_CONTAINER
CURRENT_PORT=$PORT_GREEN
NEW_CONTAINER=$BLUE_CONTAINER
NEW_PORT=$PORT_BLUE
fi
# 步骤3: 启动新版本容器
echo"启动新版本容器: ${NEW_CONTAINER}"
docker run -d \
--name $NEW_CONTAINER \
-p $NEW_PORT:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
--restart always \
$IMAGE
# 步骤4: 新版本容器健康检查
echo"等待新版本容器健康检查通过..."
for i in {1..30}; do
if curl -s http://localhost:$NEW_PORT/actuator/health | grep -q "UP"; then
echo"新版本容器健康检查通过"
break
fi
if [ $i -eq 30 ]; then
echo"新版本容器健康检查失败,执行回滚"
docker stop $NEW_CONTAINER
docker rm $NEW_CONTAINER
exit 1
fi
sleep 2
done
# 步骤5: 切换Nginx流量到新版本
echo"切换流量到新版本容器"
sed -i "s/127.0.0.1:$CURRENT_PORT/127.0.0.1:$NEW_PORT/g"$NGINX_CONF
nginx -s reload
# 步骤6: 停止并清理旧版本容器
echo"清理旧版本容器"
docker stop $CURRENT_CONTAINER
docker rm $CURRENT_CONTAINER
echo"蓝绿部署完成,服务已更新至最新版本"
该脚本的执行流程完全符合蓝绿部署的核心逻辑:先启动新版本容器,完成健康检查确认服务正常后,再切换Nginx流量,最后清理旧版本容器。如果新版本健康检查不通过,会自动回滚,删除新版本容器,不会影响线上服务,实现真正的零停机发布。
CI/CD不是一套简单的工具,而是一套完整的研发交付方法论,它的核心目标是通过自动化的流程,把代码从提交到发布的全流程标准化、可视化、低风险化,让开发者从繁琐的手动部署中解放出来,专注于业务代码的开发,同时大幅提升系统的稳定性和交付效率。一套完善的CI/CD体系,能让团队的交付效率提升数倍,同时将线上发布的风险降到最低,真正实现“随时可发布,发布不加班”的研发状态。