职业杀手
26.53M · 2026-03-25
Java 作为企业级开发的首选语言,凭借完善的生态、强类型安全保障与成熟的企业级特性,在过去二十多年里始终占据着开发语言的主流地位。但传统 Java 应用的部署模式,无论是单体应用还是微服务架构,都始终面临着服务器运维成本高、资源利用率低、弹性伸缩能力弱、发布周期长等核心痛点。
Serverless 架构的出现,为 Java 开发者打开了全新的思路。但行业内长期存在“Java 冷启动慢,不适合 Serverless”的刻板印象,让很多 Java 开发者对这一架构望而却步。
根据 CNCF(云原生计算基金会)的权威定义,Serverless 是一种构建和运行应用程序的范式,它消除了基础设施管理的复杂性,让开发者无需关注服务器的运维、扩容、容灾等底层工作,只需聚焦于业务代码本身。
Serverless 架构包含两大核心形态,二者相辅相成构成完整的 Serverless 体系:
Serverless 的核心特征可以总结为四点:
很多开发者对 Java 在 Serverless 场景的适配性存在误解,事实上,Java 不仅能适配 Serverless 架构,还能凭借自身的特性,在企业级 Serverless 场景中发挥独特的优势:
Java 在 Serverless 场景的核心挑战集中在冷启动问题,这也是行业内对 Java Serverless 最大的顾虑。要解决这个问题,首先要彻底理解 Java 冷启动的底层逻辑。
冷启动,指的是当事件触发时,平台需要从零开始启动一个全新的函数实例,完成 JVM 初始化、类加载、应用初始化、业务代码执行的全流程。Java 冷启动耗时较长的核心原因,来自于 JVM 的启动流程与 Java 应用的初始化逻辑:
除了冷启动之外,Java 应用的内存占用相对较高,而 FaaS 平台大多按照内存规格与执行时间计费,这也会对 Java Serverless 应用的成本产生一定影响。后续章节会针对这些挑战,提供完整的、可落地的优化方案。
冷启动优化是 Java Serverless 落地的核心,目前行业内已经形成了一套完整的优化体系,从编译期、运行时、启动流程、JVM 参数多个维度,彻底解决 Java 冷启动的痛点。
GraalVM 是 Oracle 推出的高性能多语言运行时,其核心能力之一就是 AOT(提前编译),可以将 Java 字节码直接编译为对应平台的机器码,生成独立的可执行文件,也就是原生镜像(Native Image)。
传统的 Java 应用运行在 JVM 之上,启动时需要完成 JVM 初始化、类加载、字节码解释执行等全流程。而 GraalVM AOT 编译在应用构建阶段,就完成了所有的类加载、字节码验证、静态分析,将应用所有用到的代码、依赖的类库、甚至精简后的 JVM 核心组件,全部编译为机器码,生成一个独立的可执行文件。
这个过程中,GraalVM 会通过静态分析实现代码裁剪(Tree Shaking),剔除所有未被使用的类、方法、字段,大幅减小应用的体积。运行时,无需启动完整的 JVM,无需类加载,无需解释执行,直接运行机器码,启动耗时可以从传统的数秒降低到百毫秒以内,甚至十毫秒级别。
GraalVM 原生镜像适合对冷启动延迟要求极高的在线业务场景,比如面向用户的 HTTP 接口。但需要注意,AOT 编译是基于静态分析的,对于 Java 的反射、动态代理、动态类加载等动态特性,需要在编译前通过配置文件明确声明,否则会出现运行时异常。目前 Spring Boot 3+、MyBatis-Plus 等主流框架已经原生支持 GraalVM 原生镜像,大幅降低了开发成本。
CRaC(Coordinated Restore at Checkpoint)是 OpenJDK 主导的开源项目,核心是通过运行时快照技术,彻底解决 Java 冷启动问题。
CRaC 的核心逻辑非常简单:在 Java 应用完全启动完成、所有初始化工作都执行完毕后,对整个 JVM 的运行时状态做一个完整的快照,包括堆内存、线程状态、类加载信息、文件句柄、网络连接等所有运行时数据,将快照持久化到磁盘。
当需要启动新的应用实例时,无需重新走 JVM 启动、类加载、应用初始化的全流程,直接从快照中恢复 JVM 的运行时状态,瞬间完成实例启动。整个恢复过程的耗时可以控制在 10 毫秒以内,完全消除了冷启动的性能损耗。
目前主流云厂商的 FaaS 平台,已经基于快照技术推出了对应的冷启动优化方案,开发者无需修改代码,直接开启即可使用:
对于无法使用原生镜像或快照方案的场景,也可以通过优化类加载与启动流程,大幅降低冷启动耗时。
AppCDS(Application Class Data Sharing)是 JDK 内置的类加载优化技术,核心是将应用的类加载数据缓存起来,实现跨 JVM 实例的共享复用。
传统的类加载流程中,每个 JVM 实例都需要单独完成类的读取、验证、解析,生成对应的类元数据。而 AppCDS 可以在应用首次启动时,将所有核心类的元数据缓存到共享归档文件中,后续启动的 JVM 实例,直接读取归档文件中的类元数据,无需重新执行类加载的全流程,类加载耗时可以降低 50% 以上。
懒加载的核心逻辑,是将非启动必需的初始化工作,从应用启动阶段推迟到实际使用时执行,减少启动阶段的时间开销。
最常用的懒加载优化,是开启 Spring Boot 的全局懒加载,只需在配置文件中添加 spring.main.lazy-initialization=true,即可让所有 Bean 的实例化与依赖注入,推迟到第一次被访问时执行,而非启动时一次性完成。对于 Spring Cloud Function 应用,还可以开启函数级别的懒加载,只有对应事件触发时,才初始化对应的函数 Bean,大幅减少启动时的初始化工作量。
针对 Serverless 场景的特点,调整 JVM 运行时参数,也可以有效降低冷启动耗时,优化运行时性能:
-XX:TieredStopAtLevel=1 参数,关闭 C2 编译器,只使用 C1 编译器,减少启动阶段的编译开销,启动速度提升 30% 以上。-Xmx 与 -Xms 设置为相同的值,避免 JVM 运行时动态调整堆内存大小,减少 GC 开销与内存抖动。同时根据函数的实际内存需求,合理设置堆内存大小,避免内存浪费。-XX:+UseZGC 参数开启。ZGC 的最大停顿时间不超过 10 毫秒,不会因为 GC 停顿导致函数执行超时,非常适合 Serverless 场景。-Xverify:none 参数关闭字节码验证,减少类加载阶段的时间开销。需要注意,该参数仅适用于已经经过测试的可信应用,避免安全风险。Java 函数计算应用的通用架构,遵循事件驱动的核心逻辑,整体分为事件源、函数计算平台、函数执行环境、业务函数、BaaS 服务五大核心组件,架构图如下:
各组件的核心职责如下:
Serverless 架构的核心是事件驱动,函数的执行完全由事件触发,而非主动轮询或持续运行。Java 函数的事件驱动架构设计,需要遵循以下核心原则:
事件驱动架构的核心优势,是实现了业务逻辑的完全解耦,每个函数都是独立的、可独立部署、独立扩缩容的单元,系统的可扩展性、可维护性大幅提升。同时,事件驱动架构天然适配 Serverless 的按需付费、自动弹性伸缩的特性,资源利用率最大化。
函数拆分是 Java Serverless 架构设计的核心环节,拆分粒度过粗,会导致函数代码臃肿、冷启动慢、职责不清晰;拆分粒度过细,会导致函数数量爆炸、调用链复杂、运维成本高、链路延迟增加。
Java 函数拆分需要遵循以下四大核心原则:
虽然函数是最小的部署单元,代码量相对较小,但依然需要清晰的代码分层,避免代码混乱、可维护性差。Java 函数的内部代码,推荐采用四层架构设计,从外到内依次为:
这种分层架构,完全符合 Java 企业级开发的规范,代码的可维护性、可测试性、可复用性都有保障,同时也符合 Serverless 函数的轻量化特性。
Java Serverless 的落地,首先要选择合适的运行平台。目前主流的平台分为两大类:商用云厂商 FaaS 平台、开源 Serverless 平台,不同的平台有不同的特性、适用场景,开发者需要根据自身的业务需求,选择最合适的平台。
商用云厂商的 FaaS 平台,是目前企业级 Java Serverless 应用的首选,平台提供了完整的托管能力、丰富的事件源集成、完善的可观测性工具,开发者无需关注底层基础设施,只需聚焦于业务代码开发。
主流商用 FaaS 平台的核心特性对比如下:
| 平台名称 | Java 支持度 | 冷启动优化能力 | Spring 生态适配 | 事件源丰富度 | 核心优势 | 适用场景 |
|---|---|---|---|---|---|---|
| AWS Lambda | 非常完善,官方深度支持 | SnapStart 快照、GraalVM 原生镜像、预置并发 | 官方适配 Spring Cloud Function | 极丰富,覆盖 AWS 全生态服务 | 生态最成熟,全球化部署能力强,稳定性高 | 出海业务、全球化部署、AWS 生态用户 |
| 阿里云函数计算 FC | 非常完善,国内支持度最高 | 镜像快照、预留实例、弹性实例、GraalVM 原生镜像 | 深度适配 Spring Boot、Spring Cloud | 丰富,覆盖阿里云全生态服务 | 国内市场份额最高,文档完善,本地化服务好 | 国内业务、阿里云生态用户、企业级核心业务 |
| 腾讯云 SCF | 完善 | 预置并发、镜像加速、GraalVM 原生镜像 | 适配 Spring Boot、Spring Cloud | 丰富,覆盖腾讯云全生态,深度集成微信生态 | 微信生态集成能力强,使用门槛低 | 微信生态业务、小程序后端、腾讯云生态用户 |
| 华为云 FunctionGraph | 完善 | 预留实例、快照加速、CRaC 支持 | 适配 Spring Boot、Spring Cloud | 丰富,覆盖华为云全生态 | 政企支持能力强,安全合规能力完善 | 政企客户、华为云生态用户、国产化业务 |
对于有私有化部署需求、不想被云厂商锁定、需要跨云部署的企业,开源 Serverless 平台是最佳选择。目前主流的开源 Serverless 平台如下:
Java Serverless 平台的选型,需要结合自身的业务需求,综合考虑以下六大核心因素:
本章节将通过完整的项目,讲解 Java Serverless 应用的开发流程、代码规范、部署方式,所有代码均基于 JDK 17 编写,适配主流 FaaS 平台。
项目采用 Maven 进行依赖管理,基于 Spring Boot 3.x、Spring Cloud Function 开发,Spring Cloud Function 是 Spring 官方推出的函数式编程框架,完美适配 Serverless 架构,实现了业务代码与 FaaS 平台的解耦,一套代码可以无缝部署到阿里云 FC、AWS Lambda 等多个平台。
完整的 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 d">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<groupId>com.jam.demo</groupId>
<artifactId>serverless-java-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>serverless-java-demo</name>
<description>Java Serverless Demo Project</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.1</spring-cloud.version>
<mybatis-plus.version>3.5.7</mybatis-plus.version>
<mysql.version>8.0.37</mysql.version>
<fastjson2.version>2.0.52</fastjson2.version>
<guava.version>33.1.0-jre</guava.version>
<springdoc.version>3.0.0</springdoc.version>
<lombok.version>1.18.32</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
项目采用 MySQL 8.0 作为数据库,使用 MyBatis-Plus 作为持久层框架,用户表的建表语句如下:
CREATE TABLE `t_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(64) NOT NULL COMMENT '用户名',
`email` varchar(128) NOT NULL COMMENT '邮箱',
`phone` varchar(11) DEFAULT NULL COMMENT '手机号',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除 0-未删除 1-已删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';
统一的接口响应格式,包名 com.jam.demo.common:
package com.jam.demo.common;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 通用响应类
*
* @author ken
* @since 2026-03-24
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "通用响应结果")
public class Result<T> {
@Schema(description = "响应码", example = "200")
private Integer code;
@Schema(description = "响应消息", example = "操作成功")
private String message;
@Schema(description = "响应数据")
private T data;
/**
* 成功响应
*
* @param data 响应数据
* @param <T> 数据类型
* @return 响应结果
*/
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
/**
* 失败响应
*
* @param code 响应码
* @param message 错误消息
* @param <T> 数据类型
* @return 响应结果
*/
public static <T> Result<T> fail(Integer code, String message) {
return new Result<>(code, message, null);
}
}
对应数据库表结构,包名 com.jam.demo.entity:
package com.jam.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用户实体类
*
* @author ken
* @since 2026-03-24
*/
@Data
@TableName("t_user")
@Schema(description = "用户实体")
public class User {
@Schema(description = "用户ID", example = "1")
@TableId(type = IdType.AUTO)
private Long id;
@Schema(description = "用户名", example = "jam_demo")
@TableField("username")
private String username;
@Schema(description = "邮箱", example = "jam@demo.com")
@TableField("email")
private String email;
@Schema(description = "手机号", example = "13800138000")
@TableField("phone")
private String phone;
@Schema(description = "创建时间")
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Schema(description = "更新时间")
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@Schema(description = "是否删除")
@TableLogic
@TableField("is_deleted")
private Integer isDeleted;
}
基于 MyBatis-Plus 实现,包名 com.jam.demo.mapper:
package com.jam.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户Mapper接口
*
* @author ken
* @since 2026-03-24
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
配置分页插件与字段自动填充,包名 com.jam.demo.config:
package com.jam.demo.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
/**
* MyBatis-Plus配置类
*
* @author ken
* @since 2026-03-24
*/
@Configuration
public class MybatisPlusConfig implements MetaObjectHandler {
/**
* 配置MyBatis-Plus拦截器
*
* @return 拦截器实例
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
Swagger3 接口文档配置,包名 com.jam.demo.config:
package com.jam.demo.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* OpenAPI配置类
*
* @author ken
* @since 2026-03-24
*/
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("Java Serverless Demo API")
.description("Java Serverless 函数计算示例接口文档")
.version("1.0.0"));
}
}
application.yml 配置,放在 resources 目录下:
spring:
application:
name: serverless-java-demo
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/serverless_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: root
cloud:
function:
definition: getUserById;createUser;handleFileUploadEvent
main:
lazy-initialization: true
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
logic-delete-field: isDeleted
logic-delete-value: 1
logic-not-delete-value: 0
springdoc:
api-docs:
enabled: true
swagger-ui:
enabled: true
path: /swagger-ui.html
Spring Cloud Function 提供了三个核心函数式接口,对应不同的业务场景:
Function<T, R>:接收一个输入参数,返回一个结果,对应同步请求场景,如 HTTP 接口。Consumer<T>:接收一个输入参数,无返回值,对应异步事件处理场景,如消息消费、文件事件处理。Supplier<T>:无输入参数,返回一个结果,对应定时任务、数据拉取场景。实现 Function<Long, Result<User>> 接口,包名 com.jam.demo.function:
package com.jam.demo.function;
import com.jam.demo.common.Result;
import com.jam.demo.entity.User;
import com.jam.demo.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.util.function.Function;
/**
* 根据ID查询用户函数
*
* @author ken
* @since 2026-03-24
*/
@Slf4j
@Component("getUserById")
public class GetUserByIdFunction implements Function<Long, Result<User>> {
private final UserMapper userMapper;
public GetUserByIdFunction(UserMapper userMapper) {
this.userMapper = userMapper;
}
/**
* 函数执行方法
*
* @param userId 用户ID
* @return 用户查询结果
*/
@Override
public Result<User> apply(Long userId) {
log.info("开始查询用户,用户ID:{}", userId);
if (ObjectUtils.isEmpty(userId)) {
log.warn("用户ID为空");
return Result.fail(400, "用户ID不能为空");
}
User user = userMapper.selectById(userId);
if (ObjectUtils.isEmpty(user)) {
log.warn("用户不存在,用户ID:{}", userId);
return Result.fail(404, "用户不存在");
}
log.info("用户查询成功,用户ID:{}", userId);
return Result.success(user);
}
}
实现 Function<User, Result<Long>> 接口,使用编程式事务控制,包名 com.jam.demo.function:
package com.jam.demo.function;
import com.jam.demo.common.Result;
import com.jam.demo.entity.User;
import com.jam.demo.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.util.function.Function;
/**
* 创建用户函数
*
* @author ken
* @since 2026-03-24
*/
@Slf4j
@Component("createUser")
public class CreateUserFunction implements Function<User, Result<Long>> {
private final UserMapper userMapper;
private final TransactionTemplate transactionTemplate;
public CreateUserFunction(UserMapper userMapper, TransactionTemplate transactionTemplate) {
this.userMapper = userMapper;
this.transactionTemplate = transactionTemplate;
}
/**
* 函数执行方法
*
* @param user 用户信息
* @return 创建结果,包含用户ID
*/
@Override
public Result<Long> apply(User user) {
log.info("开始创建用户,用户名:{}", user.getUsername());
if (!StringUtils.hasText(user.getUsername())) {
log.warn("用户名不能为空");
return Result.fail(400, "用户名不能为空");
}
if (!StringUtils.hasText(user.getEmail())) {
log.warn("邮箱不能为空");
return Result.fail(400, "邮箱不能为空");
}
Long userId = transactionTemplate.execute(new TransactionCallback<Long>() {
@Override
public Long doInTransaction(TransactionStatus status) {
try {
userMapper.insert(user);
return user.getId();
} catch (Exception e) {
status.setRollbackOnly();
log.error("创建用户失败,用户名:{}", user.getUsername(), e);
return null;
}
}
});
if (ObjectUtils.isEmpty(userId)) {
return Result.fail(500, "创建用户失败");
}
log.info("用户创建成功,用户ID:{}", userId);
return Result.success(userId);
}
}
实现 Consumer<String> 接口,处理对象存储的文件上传事件,包名 com.jam.demo.function:
package com.jam.demo.function;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.function.Consumer;
/**
* 处理文件上传事件函数
*
* @author ken
* @since 2026-03-24
*/
@Slf4j
@Component("handleFileUploadEvent")
public class HandleFileUploadEventFunction implements Consumer<String> {
/**
* 事件处理方法
*
* @param eventJson 事件JSON字符串
*/
@Override
public void accept(String eventJson) {
log.info("接收到文件上传事件,事件内容:{}", eventJson);
if (!StringUtils.hasText(eventJson)) {
log.warn("事件内容为空");
return;
}
JSONObject eventObject = JSON.parseObject(eventJson);
String bucketName = eventObject.getString("bucketName");
String fileName = eventObject.getString("fileName");
String fileSize = eventObject.getString("fileSize");
if (!StringUtils.hasText(bucketName) || !StringUtils.hasText(fileName)) {
log.warn("事件参数不完整,bucketName:{},fileName:{}", bucketName, fileName);
return;
}
log.info("开始处理文件,bucket:{},文件名:{},文件大小:{}", bucketName, fileName, fileSize);
// 此处可扩展文件解析、缩略图生成、数据入库等业务逻辑
log.info("文件处理完成,bucket:{},文件名:{}", bucketName, fileName);
}
}
包名 com.jam.demo:
package com.jam.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 项目启动类
*
* @author ken
* @since 2026-03-24
*/
@SpringBootApplication
@MapperScan("com.jam.demo.mapper")
public class ServerlessJavaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ServerlessJavaDemoApplication.class, args);
}
}
Spring Cloud Function 会自动将注册的函数,暴露为 HTTP 接口,本地启动项目后,可通过以下方式调用:
GET POST ,请求体为用户信息 JSONPOST ,请求体为事件 JSONSpring Cloud Function 提供了针对不同 FaaS 平台的适配适配器,只需在 pom.xml 中添加对应平台的适配器依赖,无需修改业务代码,即可将项目部署到对应的平台:
spring-cloud-function-adapter-aws 依赖,打包为 ZIP 包上传即可。spring-cloud-function-adapter-aliyun-fc 依赖,通过镜像或 JAR 包部署即可。spring-cloud-function-adapter-tencent-scf 依赖,通过 JAR 包部署即可。针对冷启动要求极高的场景,可通过 GraalVM 编译为原生镜像,大幅降低启动耗时。具体实现步骤如下:
resources/META-INF/native-image 目录下,创建 reflect-config.json 文件,配置反射相关的类信息:[
{
"name": "com.jam.demo.entity.User",
"allDeclaredFields": true,
"allDeclaredMethods": true,
"allDeclaredConstructors": true
},
{
"name": "com.jam.demo.mapper.UserMapper",
"allDeclaredMethods": true,
"allDeclaredConstructors": true
}
]
2. 执行编译命令:安装 GraalVM JDK 17 后,在项目根目录执行 mvn native:compile 命令,完成原生镜像编译。
3. 运行镜像:编译完成后,在 target 目录下会生成独立的可执行文件,直接运行即可,启动耗时可降低至 100ms 以内。
随着 JDK 技术的不断发展与云原生生态的完善,Java 在 Serverless 场景的适配性会越来越强,未来的发展趋势主要集中在以下几个方向:
JDK 21 已经正式发布了虚拟线程(Virtual Thread)特性,彻底解决了 Java 传统平台线程的开销问题,大幅提高了 Java 应用的并发处理能力。对于 Serverless 场景的 IO 密集型业务,虚拟线程可以在极小的内存开销下,支持成千上万的并发请求,大幅提高函数的资源利用率,降低成本。未来,虚拟线程会成为 Java Serverless 应用的标配。
CRaC、AppCDS 等冷启动优化技术,会逐步成为 OpenJDK 的标准特性,JDK 会原生支持运行时快照、类数据共享等能力,无需额外的配置与适配,即可大幅降低 Java 应用的启动耗时。同时,主流云厂商的 FaaS 平台,会原生支持这些标准特性,Java 冷启动的问题会被彻底解决。
目前 Spring Boot 3+、MyBatis-Plus 等主流框架已经原生支持 GraalVM 原生镜像,未来会有更多的 Java 开源组件原生支持 GraalVM AOT 编译,原生镜像的开发成本会大幅降低,开发者无需额外的配置,即可将 Java 应用编译为原生镜像,享受毫秒级的启动速度。GraalVM 原生镜像会成为 Java Serverless 在线业务的主流部署方式。
随着 Knative、OpenFunction 等开源 Serverless 平台的不断成熟,企业会越来越多地采用开源 Serverless 平台,实现私有化部署、跨云部署,避免厂商锁定。Java 作为企业级开发的主流语言,会成为开源 Serverless 平台的核心支持语言,生态会越来越完善。
AI 大模型应用的开发,天然适合 Serverless 架构,按需付费、自动弹性伸缩的特性,完美匹配 AI 应用的流量波动特点。Java 成熟的企业级生态、强类型安全保障、完善的工具链,会在 AI 应用的开发中发挥重要作用,Java Serverless 会成为 AI 应用后端开发的主流选择。
Serverless 不是 Java 的敌人,而是 Java 开发者突破传统部署模式枷锁的全新机遇。长期以来,行业内对 Java 与 Serverless 的误解,本质上是对 Java 冷启动问题的刻板印象。而随着 JDK 技术的不断发展,GraalVM、CRaC 等优化技术的不断成熟,Java 冷启动的痛点已经被彻底解决,Java 在企业级开发中的生态优势、安全优势、稳定性优势,会在 Serverless 场景中得到充分的发挥。
对于 Java 开发者而言,Serverless 不是需要颠覆现有技术体系的全新技术,而是现有技术体系的延伸与升级。我们可以复用多年积累的 Java 开发经验、Spring 生态技术栈,只需遵循 Serverless 的架构设计原则,即可快速实现业务的 Serverless 化,享受 Serverless 带来的低运维成本、高资源利用率、极致弹性伸缩的优势。