如何将Spring Boot应用部署运行至Google Cloud Function环境?
我最近在学习Google Cloud,做了个生成随机数的Spring Boot小项目,本地运行完全正常,但部署到Cloud Function就不行。项目连接了Google Cloud SQL,我选Cloud Function是因为不想管基础设施。下面是我的代码文件:
CheckerApplication.java
package fr.checker; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication @EnableJpaRepositories(basePackages = "fr.checker.infrastructure.repository") public class CheckerApplication { public static void main(String[] args) { SpringApplication.run(CheckerApplication.class, args); } }
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>3.2.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>fr.checker</groupId> <artifactId>checker</artifactId> <version>0.0.1-SNAPSHOT</version> <name>checker</name> <description>Pulling random number</description> <properties> <java.version>20</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>com.google.cloud</groupId> <artifactId>spring-cloud-gcp-dependencies</artifactId> <version>5.0.4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>jakarta.persistence</groupId> <artifactId>jakarta.persistence-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency> <!-- GOOGLE CLOUD DEPENDENCY --> <dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-storage</artifactId> </dependency> <dependency> <groupId>com.google.cloud</groupId> <artifactId>spring-cloud-gcp-starter-sql-postgresql</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.google.cloud.sql/postgres-socket-factory --> <dependency> <groupId>com.google.cloud.sql</groupId> <artifactId>postgres-socket-factory</artifactId> <version>1.16.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-deploy-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </build> </project>
DrawingController.java
package fr.checker.presentation.controller; import fr.checker.application.service.DrawingService; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api") public class DrawingController { private final DrawingService drawingService; public DrawingController(DrawingService drawingService) { this.drawingService = drawingService; } @GetMapping("/draw") public String drawMyNumbers(@RequestParam(required = false, name = "tolerance") Integer tolerance) { if(tolerance == null || tolerance == 0) return drawingService.drawNumbers().toString(); return drawingService.drawNumbersSpecifically(tolerance).toString(); } }
DrawingFunction.java
package fr.checker.presentation.function; import com.google.cloud.functions.HttpFunction; import com.google.cloud.functions.HttpRequest; import com.google.cloud.functions.HttpResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class DrawingFunction implements HttpFunction { @Autowired DrawingController drawingController; @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { httpResponse.setStatusCode(200); httpResponse.getWriter().write(drawingController.drawMyNumbers(50)); } }
MANIFEST.MF
Main-Class: fr.checker.presentation.function.DrawingFunction
我感觉自己把Spring IoC和Cloud Function的逻辑混在一起了,不知道改哪些地方才能让应用在Cloud Function正常运行。本地运行是完全没问题的。
核心问题是Cloud Function不会自动初始化Spring容器,直接实现HttpFunction的话,Spring的依赖注入(@Autowired)根本不会生效。下面是具体修改步骤:
1. 调整依赖,适配Spring Cloud Function
Cloud Function和Spring Boot的整合需要用Spring Cloud Function的GCP适配器,同时移除不需要的Web依赖:
- 移除
spring-boot-starter-webflux(Cloud Function自带HTTP处理,不需要内嵌Web服务器) - 添加Spring Cloud Function的适配依赖,让Spring自动管理Cloud Function的上下文
修改后的pom.xml依赖部分:
<dependencies> <!-- 移除spring-boot-starter-webflux --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency> <!-- GOOGLE CLOUD 相关依赖 --> <dependency> <groupId>com.google.cloud</groupId> <artifactId>spring-cloud-gcp-starter-sql-postgresql</artifactId> </dependency> <dependency> <groupId>com.google.cloud.sql</groupId> <artifactId>postgres-socket-factory</artifactId> <version>1.16.0</version> </dependency> <!-- Spring Cloud Function 适配GCP --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-function-adapter-gcp</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-function-web</artifactId> </dependency> </dependencies>
2. 修改函数实现,让Spring管理上下文
推荐用Spring Cloud Function的标准方式,不需要直接实现HttpFunction:
方案:创建函数式Bean
新建配置类,定义一个Supplier类型的Bean,Spring Cloud Function会自动把它暴露为Cloud Function:
package fr.checker.presentation.function; import fr.checker.presentation.controller.DrawingController; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.function.Supplier; @Configuration public class DrawingFunctionConfig { private final DrawingController drawingController; public DrawingFunctionConfig(DrawingController drawingController) { this.drawingController = drawingController; } @Bean public Supplier<String> drawNumbers() { // 如需从请求获取参数,可改用Function<HttpRequest, String>类型 return () -> drawingController.drawMyNumbers(50); } }
如果一定要保留HttpFunction实现,需要手动初始化Spring上下文:
package fr.checker.presentation.function; import com.google.cloud.functions.HttpFunction; import com.google.cloud.functions.HttpRequest; import com.google.cloud.functions.HttpResponse; import fr.checker.CheckerApplication; import fr.checker.presentation.controller.DrawingController; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class DrawingFunction implements HttpFunction { private static DrawingController drawingController; static { // 初始化Spring上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CheckerApplication.class); drawingController = context.getBean(DrawingController.class); } @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { httpResponse.setStatusCode(200); // 从请求中获取tolerance参数 String toleranceParam = httpRequest.getFirstQueryParameter("tolerance").orElse("50"); Integer tolerance = Integer.parseInt(toleranceParam); httpResponse.getWriter().write(drawingController.drawMyNumbers(tolerance)); } }
注意:此方案要去掉原代码中的@Component和@Autowired,因为Spring容器是手动初始化的。
3. 修改MANIFEST.MF的Main-Class
不管用哪种方案,Main-Class都应该指向Spring Boot启动类,而非你的Function类:
Main-Class: fr.checker.CheckerApplication
4. 配置Cloud Function运行参数
部署时需要:
- 设置函数入口点:用函数式Bean方案时,入口点是
drawNumbers(即Bean名称);用手动初始化方案时,入口点是fr.checker.presentation.function.DrawingFunction - 给Cloud Function的服务账号添加Cloud SQL Client权限,确保能连接Cloud SQL
- 在环境变量中配置数据库连接信息(如
SPRING_DATASOURCE_URL、SPRING_DATASOURCE_USERNAME、SPRING_DATASOURCE_PASSWORD)
5. 优化打包方式
用Spring Boot插件打包成可执行JAR,确保Cloud Function能加载所有依赖:
在pom.xml的build/plugins中添加:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>
内容的提问来源于stack exchange,提问作者Kyle_jumpen

