Skip to main content

Springboot

Spring Boot is a rapid development scaffold based on the Spring framework, simplifying the creation and deployment process of Spring applications. It provides features such as auto-configuration, embedded servers, and production-ready feature monitoring, enabling developers to quickly build standalone, production-grade Spring applications.

This guide describes how to deploy Gin applications on CloudBase HTTP cloud functions.

For sample source code, see: cloudrun-springboot

Prerequisites

Before you begin, ensure that you have:

  • Installed JDK 8 or later
  • Installed Maven 3.6+ or Gradle
  • Have a Tencent Cloud account with CloudBase service activated
  • Have a basic understanding of Java and Spring Boot development

Step 1: Create a Spring Boot Application

💡 Tip: If you already have a Spring Boot application, you can skip this step.

Use Spring Initializr to create a project

  1. Visit start.spring.io
  2. Select the following configurations:
Project: Maven
Language: Java
Spring Boot: 2.7.18 (or the latest stable version)
Project Metadata:
- Group: com.tencent
- Artifact: cloudrun-springboot
- Name: cloudrun-springboot
- Description: Demo project for Spring Boot
- Package name: com.tencent.cloudrun
- Packaging: Jar
- Java: 8
Dependencies:
- Spring Web
- Spring Boot Actuator (Health Check)
  1. Click GENERATE to download the project zip file
  2. Extract to a local directory

Create Using Maven Commands (Optional)

mvn archetype:generate \
-DgroupId=com.tencent.cloudrun \
-DartifactId=cloudrun-springboot \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false

cd cloudrun-springboot

Configure the pom.xml file

If you use Maven commands to create the project, you need to manually configure the pom.xml file to support Spring Boot:

<?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>2.7.18</version>
<relativePath/>
</parent>

<groupId>com.tencent.cloudrun</groupId>
<artifactId>cloudrun-springboot</artifactId>
<version>1.0-SNAPSHOT</version>
<name>cloudrun-springboot</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Create the Main Application Class

If you use Maven commands to create the project, you also need to create the Spring Boot main application class.

Create CloudrunApplication.java in the src/main/java/com/tencent/cloudrun directory:

package com.tencent.cloudrun;

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

@SpringBootApplication
public class CloudrunApplication {

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

Test the Application Locally

Go to the project directory and start the application:

cd cloudrun-springboot
mvn spring-boot:run

Open your browser and visit http://localhost:8080. You should see the default Spring Boot page.

Step 2: Add API Interface

Let's create some RESTful APIs to demonstrate the features of Spring Boot.

Create the User Entity Class

Create User.java in the src/main/java/com/tencent/cloudrun/entity directory:

package com.tencent.cloudrun.entity;

public class User {
private Long id;
private String name;
private String email;

// Constructor
public User() {}

public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}

// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}

Create the Response Wrapper Class

Create ApiResponse.java in the src/main/java/com/tencent/cloudrun/dto directory:

package com.tencent.cloudrun.dto;

public class ApiResponse<T> {
private boolean success;
private String message;
private T data;

public ApiResponse(boolean success, String message, T data) {
this.success = success;
this.message = message;
this.data = data;
}

public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(true, "Operation succeeded", data);
}

public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(false, message, null);
}

// Getters and Setters
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }

public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }

public T getData() { return data; }
public void setData(T data) { this.data = data; }
}

Create the User Controller

Create UserController.java in the src/main/java/com/tencent/cloudrun/controller directory:

package com.tencent.cloudrun.controller;

import com.tencent.cloudrun.dto.ApiResponse;
import com.tencent.cloudrun.entity.User;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/users")
public class UserController {

private final List<User> users = new ArrayList<>();
private final AtomicLong counter = new AtomicLong();

public UserController() {
// Initialize test data
users.add(new User(counter.incrementAndGet(), "Zhang San", "zhangsan@example.com"));
users.add(new User(counter.incrementAndGet(), "Li Si", "lisi@example.com"));
users.add(new User(counter.incrementAndGet(), "Wang Wu", "wangwu@example.com"));
}

@GetMapping
public ApiResponse<List<User>> getAllUsers(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int limit) {

int startIndex = (page - 1) * limit;
int endIndex = Math.min(startIndex + limit, users.size());

if (startIndex >= users.size()) {
return ApiResponse.success(new ArrayList<>());
}

List<User> paginatedUsers = users.subList(startIndex, endIndex);
return ApiResponse.success(paginatedUsers);
}

@GetMapping("/{id}")
public ApiResponse<User> getUserById(@PathVariable Long id) {
User user = users.stream()
.filter(u -> u.getId().equals(id))
.findFirst()
.orElse(null);

if (user == null) {
return ApiResponse.error("User not found");
}

return ApiResponse.success(user);
}

@PostMapping
public ApiResponse<User> createUser(@RequestBody User user) {
if (user.getName() == null || user.getEmail() == null) {
return ApiResponse.error("Name and email cannot be empty");
}

user.setId(counter.incrementAndGet());
users.add(user);

return ApiResponse.success(user);
}
}

Create the Health Check Controller

Create HealthController.java in the src/main/java/com/tencent/cloudrun/controller directory:

package com.tencent.cloudrun.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@RestController
public class HealthController {

@GetMapping("/health")
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<>();
health.put("status", "healthy");
health.put("timestamp", LocalDateTime.now());
health.put("framework", "Spring Boot");
health.put("version", getClass().getPackage().getImplementationVersion());
health.put("java_version", System.getProperty("java.version"));

return health;
}

@GetMapping("/")
public Map<String, String> home() {
Map<String, String> response = new HashMap<>();
response.put("message", "Welcome to the Spring Boot CloudBase application!");
response.put("status", "running");

return response;
}
}

Configure Application Properties

Edit src/main/resources/application.properties:

# Server Configuration
server.port=${PORT:8080}
server.servlet.context-path=/

# Application Configuration
spring.application.name=cloudrun-springboot
management.endpoints.web.exposure.include=health,info

# Logging Configuration
logging.level.com.tencent.cloudrun=INFO
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n

Step 3: Local Testing

Start the Application

mvn spring-boot:run

Test the API Interface

# Testing Health Check
curl http://localhost:8080/health

# Home Page Testing
curl http://localhost:8080/

# Testing User List
curl http://localhost:8080/api/users

# Pagination Testing
curl "http://localhost:8080/api/users?page=1&limit=2"

# Testing Obtaining a Single User
curl http://localhost:8080/api/users/1

# Testing Creating a User
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name":"New User","email":"newuser@example.com"}'

Step 4: Prepare Deployment Files

HTTP cloud functions require the scf_bootstrap startup script and specific port configuration.

1. Modify Application Configuration

Edit src/main/resources/application.properties to ensure the cloud function environment uses port 9000:

# Cloud Function Port Configuration (default 8080, cloud function environment uses 9000)
server.port=${PORT:8080}
server.servlet.context-path=/

# Application Configuration
spring.application.name=cloudrun-springboot
management.endpoints.web.exposure.include=health,info

⚠️ Important Note: CloudBase HTTP cloud functions require the application to listen on port 9000, controlled via the environment variable PORT=9000.

2. Create Startup Script

Create the scf_bootstrap file (without an extension):

#!/bin/bash
export PORT=9000
export JAVA_OPTS="-Xmx512m -Xms256m -XX:+UseG1GC"
java $JAVA_OPTS -jar *.jar

💡 Note:

  • Use the *.jar wildcard to match JAR packages in the current directory
  • CloudBase cloud function will extract the uploaded files to the working directory, and JAR packages are directly located in the current directory.

Grant execute permission to the startup script:

chmod +x scf_bootstrap

3. Build JAR Package

mvn clean package -DskipTests

4. Project Structure

cloudrun-springboot/
├── src/
│ └── main/
│ ├── java/
│ └── resources/
├── target/
│ └── *.jar # Build artifact (such as: cloudrun-springboot-1.0-SNAPSHOT.jar)
├── pom.xml
├── scf_bootstrap # 🔑 Cloud function startup script
└── README.md

💡 Note:

  • scf_bootstrap is the startup script for CloudBase cloud functions
  • The cloud function will extract the uploaded files to the working directory, and JAR packages are directly located in the current directory.
  • Set the PORT=9000 environment variable to ensure the application listens on the correct port
  • Configure JVM parameters to optimize memory usage

Step 5: Deploy Application

Deployment via the Console

⚠️ Note:

  • java services start up slowly; it is recommended to adjust the function execution timeout to 30s
  1. Log in to the CloudBase Console
  2. Select your environment and go to the cloud function page
  3. Click "Create New Cloud Function"
  4. Select "HTTP Cloud Function"
  5. Fill in the function name (e.g.: springboot-app)
  6. Select runtime: Java 8 (or other supported versions)
  7. Select submission method: Local upload of code package
  8. Upload the built JAR package and the scf_bootstrap file
  9. Automatic dependency installation: Disable (Not required for Java applications)
  10. Click the "Create" button and wait for deployment to complete

Deployment via CLI (Coming Soon)

Package and deploy

If manual packaging is required:

# Build Application
mvn clean package -DskipTests

# Create Deployment Package (use the -j parameter to avoid directory structures)
zip -j springboot-app.zip target/*.jar scf_bootstrap

Step 6: Access Your Application

After successful deployment, you can refer to Accessing Cloud Functions via HTTP to set up a custom domain for accessing HTTP Cloud Functions.

Example Request

# Health Check
curl https://your-app-url/health

# Obtain User List
curl https://your-app-url/api/users

# Pagination Query
curl "https://your-app-url/api/users?page=1&limit=2"

# Create New User
curl -X POST https://your-app-url/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Test User","email":"test@example.com"}'

Frequently Asked Questions

Q: Why use *.jar instead of a specific JAR package name?

A: Using wildcards has the following advantages:

  • Avoid path errors caused by changes in the project version number
  • Simplify the deployment process without the need to modify the startup script each time
  • Ensure that the built JAR package can be located regardless of its specific name
  • The cloud function will extract the files to the current working directory; use relative paths.

Q: What if there are no JAR packages in the current directory?

A: Check the following points:

  1. Ensure to use the zip -j parameter for packaging to avoid directory structure issues
  2. Verify that the mvn clean package command executed successfully
  3. Check whether the JAR package has been generated in the target directory
  4. Ensure that the JAR package file is included during packaging

Q: Why use the zip -j parameter?

A: The -j parameter serves to "junk paths" (ignore directory structures):

  • Using zip -j ignores the directory structure of files, saving only the files themselves
  • In this way, target/cloudrun-springboot-1.0-SNAPSHOT.jar will directly become cloudrun-springboot-1.0-SNAPSHOT.jar after extraction
  • Avoids the issue of multi-level directories like /opt/target/ in cloud functions
  • Simplifies the packaging process without the need to manually copy and delete files

Q: What is the /opt directory in scf_bootstrap?

A: /opt is the standard working directory for the CloudBase cloud function runtime environment:

  • When you upload a code package to a cloud function, all files will be extracted to the /opt directory
  • JAR packages, configuration files, etc., will all be located under the /opt directory
  • This is the standard convention of the cloud function platform and does not require manual creation
  • In the startup script, the absolute path /opt/your-jar-file.jar must be used to reference the JAR package

Q: Why must HTTP cloud functions use port 9000?

A: CloudBase HTTP cloud functions require the application to listen on port 9000, which is the standard configuration of the platform. The application controls the port through the environment variable PORT=9000, while using port 8080 by default during local development.

Q: How to handle long cold startup times for Spring Boot applications?

A: You can optimize in the following ways:

  • Enable spring.main.lazy-initialization=true
  • Reduce auto-configured components
  • Use GraalVM native images (experimental)
  • Configure JVM parameters properly

Q: When running mvn spring-boot:run, it prompts "No plugin found for prefix 'spring-boot'", how to handle it?

A: This is because the project lacks Spring Boot Maven plugin configuration. Solution:

  1. Ensure pom.xml contains spring-boot-starter-parent as the parent project.
  2. Add spring-boot-maven-plugin in the <build><plugins> section
  3. Refer to the complete pom.xml configuration example in the documentation

Q: How to handle the issue of excessively large JAR packages?

A:

  • Use the repackage goal of the spring-boot-maven-plugin
  • Exclude unnecessary dependencies
  • Use the provided scope to exclude dependencies not required at runtime

Q: How to view Spring Boot logs in cloud functions?

A: On the CloudBase Console's cloud function page, click the function name to go to the details page and view the runtime logs.

Best Practices

1. Environment Variables Management

Use environment variables in application.properties:

# Database Configuration
spring.datasource.url=${DB_URL:jdbc:h2:mem:testdb}
spring.datasource.username=${DB_USERNAME:sa}
spring.datasource.password=${DB_PASSWORD:}

# Application Configuration
app.jwt.secret=${JWT_SECRET:mySecret}
app.upload.path=${UPLOAD_PATH:/tmp/uploads}

2. Port Configuration Policy

To support both deployment approaches, it is recommended to use dynamic port configuration:

# Support for Multi-Environment Port Configuration (default 8080, cloud function environment controlled via PORT=9000)
server.port=${PORT:8080}

# Cloud Function Environment Detection
spring.profiles.active=${SPRING_PROFILES_ACTIVE:default}

3. Add Global Exception Handling

Create GlobalExceptionHandler.java:

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
public ApiResponse<String> handleException(Exception e) {
return ApiResponse.error("System error: " + e.getMessage());
}

@ExceptionHandler(IllegalArgumentException.class)
public ApiResponse<String> handleIllegalArgument(IllegalArgumentException e) {
return ApiResponse.error("Parameter error: " + e.getMessage());
}
}

4. Configure CORS Support

Create WebConfig.java:

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*");
}
}

5. JVM Parameter Optimization

JVM Optimization for Different Deployment Approaches:

# HTTP Cloud Function (Memory-Constrained)
JAVA_OPTS="-Xmx512m -Xms256m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"

# Cloud Hosting (More Resources)
JAVA_OPTS="-Xmx1g -Xms512m -XX:+UseG1GC"

6. Health Check Enhancement

Custom Health Check Indicator:

@Component
public class CustomHealthIndicator implements HealthIndicator {

@Override
public Health health() {
// Check application status
boolean isHealthy = checkApplicationHealth();

if (isHealthy) {
return Health.up()
.withDetail("status", "Application running normally")
.withDetail("timestamp", LocalDateTime.now())
.build();
} else {
return Health.down()
.withDetail("status", "Application abnormal")
.build();
}
}

private boolean checkApplicationHealth() {
// Implement specific health check logic
return true;
}
}

7. Pre-deployment Checklist

  • The scf_bootstrap file exists and has execute permission
  • Port configured to 9000
  • JAR package built successfully and executable
  • JVM parameters suitable for cloud function environments
  • Enable lazy loading to optimize cold startup
  • Test whether local startup functions properly

Next Steps