1. 创建第一个应用
1.1 项目结构
spring-native-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── demo/
│ │ │ ├── Application.java
│ │ │ ├── config/
│ │ │ │ ├── WebConfig.java
│ │ │ │ └── NativeConfig.java
│ │ │ ├── controller/
│ │ │ │ ├── UserController.java
│ │ │ │ └── HealthController.java
│ │ │ ├── service/
│ │ │ │ ├── UserService.java
│ │ │ │ └── UserServiceImpl.java
│ │ │ ├── repository/
│ │ │ │ └── UserRepository.java
│ │ │ ├── model/
│ │ │ │ └── User.java
│ │ │ └── dto/
│ │ │ ├── UserDto.java
│ │ │ └── CreateUserRequest.java
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── native-image/
│ │ │ ├── reflect-config.json
│ │ │ ├── resource-config.json
│ │ │ ├── serialization-config.json
│ │ │ └── proxy-config.json
│ │ ├── static/
│ │ │ └── index.html
│ │ └── application.yml
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ └── demo/
│ ├── ApplicationTests.java
│ ├── controller/
│ │ └── UserControllerTest.java
│ └── service/
│ └── UserServiceTest.java
├── pom.xml
└── README.md
1.2 主应用类
// src/main/java/com/example/demo/Application.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.nativex.hint.NativeHint;
import org.springframework.nativex.hint.TypeHint;
import org.springframework.nativex.hint.TypeAccess;
import org.springframework.nativex.hint.ResourceHint;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Spring Native Demo 应用主类
*
* 使用 @NativeHint 注解提供原生编译所需的配置信息
*/
@SpringBootApplication
@EnableJpaRepositories
@EnableTransactionManagement
@NativeHint(
// 类型提示:指定需要反射访问的类
types = {
@TypeHint(
types = {
com.example.demo.model.User.class,
com.example.demo.dto.UserDto.class,
com.example.demo.dto.CreateUserRequest.class
},
access = {
TypeAccess.DECLARED_CONSTRUCTORS,
TypeAccess.DECLARED_METHODS,
TypeAccess.DECLARED_FIELDS
}
),
// Jackson 序列化相关类
@TypeHint(
types = {
com.fasterxml.jackson.databind.ObjectMapper.class,
com.fasterxml.jackson.core.JsonGenerator.class,
com.fasterxml.jackson.core.JsonParser.class
},
access = TypeAccess.DECLARED_CONSTRUCTORS
),
// JPA 相关类
@TypeHint(
types = {
org.hibernate.dialect.H2Dialect.class,
org.h2.Driver.class
},
access = TypeAccess.DECLARED_CONSTRUCTORS
)
},
// 资源提示:指定需要包含的资源文件
resources = {
@ResourceHint(patterns = {
"application.yml",
"application-*.yml",
"static/**",
"META-INF/spring.factories"
})
}
)
public class Application {
public static void main(String[] args) {
// 记录启动时间
long startTime = System.currentTimeMillis();
SpringApplication app = new SpringApplication(Application.class);
// 设置应用属性
app.setAdditionalProfiles("native");
// 启动应用
var context = app.run(args);
long endTime = System.currentTimeMillis();
System.out.printf("应用启动完成,耗时: %d ms%n", endTime - startTime);
// 输出应用信息
String port = context.getEnvironment().getProperty("server.port", "8080");
System.out.printf("应用已启动,访问地址: http://localhost:%s%n", port);
System.out.printf("健康检查: http://localhost:%s/actuator/health%n", port);
System.out.printf("API 文档: http://localhost:%s/api/users%n", port);
}
}
1.3 实体模型
// src/main/java/com/example/demo/model/User.java
package com.example.demo.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 用户实体类
*
* 使用 JPA 注解进行数据库映射
* 包含基本的用户信息和审计字段
*/
@Entity
@Table(name = "users", indexes = {
@Index(name = "idx_user_email", columnList = "email", unique = true),
@Index(name = "idx_user_username", columnList = "username", unique = true)
})
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间")
@Column(name = "username", nullable = false, unique = true, length = 50)
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
@Column(name = "email", nullable = false, unique = true, length = 100)
private String email;
@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码长度至少6个字符")
@Column(name = "password", nullable = false)
private String password;
@Size(max = 100, message = "姓名长度不能超过100个字符")
@Column(name = "full_name", length = 100)
private String fullName;
@Min(value = 0, message = "年龄不能为负数")
@Max(value = 150, message = "年龄不能超过150")
@Column(name = "age")
private Integer age;
@Column(name = "active", nullable = false)
private Boolean active = true;
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
// 默认构造函数(JPA 需要)
public User() {
}
// 构造函数
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
public User(String username, String email, String password, String fullName, Integer age) {
this(username, email, password);
this.fullName = fullName;
this.age = age;
}
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
// 业务方法
public void activate() {
this.active = true;
}
public void deactivate() {
this.active = false;
}
public boolean isActive() {
return Boolean.TRUE.equals(this.active);
}
// equals, hashCode, toString
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(username, user.username) &&
Objects.equals(email, user.email);
}
@Override
public int hashCode() {
return Objects.hash(id, username, email);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", fullName='" + fullName + '\'' +
", age=" + age +
", active=" + active +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}
1.4 数据传输对象
// src/main/java/com/example/demo/dto/UserDto.java
package com.example.demo.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.*;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 用户数据传输对象
*
* 用于 API 响应,不包含敏感信息如密码
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserDto {
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@Size(max = 100, message = "姓名长度不能超过100个字符")
private String fullName;
@Min(value = 0, message = "年龄不能为负数")
@Max(value = 150, message = "年龄不能超过150")
private Integer age;
private Boolean active;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updatedAt;
// 默认构造函数
public UserDto() {
}
// 构造函数
public UserDto(Long id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
public UserDto(Long id, String username, String email, String fullName,
Integer age, Boolean active, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.username = username;
this.email = email;
this.fullName = fullName;
this.age = age;
this.active = active;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
// equals, hashCode, toString
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserDto userDto = (UserDto) o;
return Objects.equals(id, userDto.id) &&
Objects.equals(username, userDto.username) &&
Objects.equals(email, userDto.email);
}
@Override
public int hashCode() {
return Objects.hash(id, username, email);
}
@Override
public String toString() {
return "UserDto{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", fullName='" + fullName + '\'' +
", age=" + age +
", active=" + active +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}
// src/main/java/com/example/demo/dto/CreateUserRequest.java
package com.example.demo.dto;
import jakarta.validation.constraints.*;
/**
* 创建用户请求对象
*/
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码长度至少6个字符")
private String password;
@Size(max = 100, message = "姓名长度不能超过100个字符")
private String fullName;
@Min(value = 0, message = "年龄不能为负数")
@Max(value = 150, message = "年龄不能超过150")
private Integer age;
// 默认构造函数
public CreateUserRequest() {
}
// 构造函数
public CreateUserRequest(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
public CreateUserRequest(String username, String email, String password,
String fullName, Integer age) {
this(username, email, password);
this.fullName = fullName;
this.age = age;
}
// Getters and Setters
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "CreateUserRequest{" +
"username='" + username + '\'' +
", email='" + email + '\'' +
", fullName='" + fullName + '\'' +
", age=" + age +
'}';
}
}
1.5 数据访问层
// src/main/java/com/example/demo/repository/UserRepository.java
package com.example.demo.repository;
import com.example.demo.model.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
/**
* 用户数据访问接口
*
* 继承 JpaRepository 提供基本的 CRUD 操作
* 定义自定义查询方法
*/
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
/**
* 根据用户名查找用户
*/
Optional<User> findByUsername(String username);
/**
* 根据邮箱查找用户
*/
Optional<User> findByEmail(String email);
/**
* 检查用户名是否存在
*/
boolean existsByUsername(String username);
/**
* 检查邮箱是否存在
*/
boolean existsByEmail(String email);
/**
* 根据激活状态查找用户
*/
List<User> findByActive(Boolean active);
/**
* 根据激活状态分页查找用户
*/
Page<User> findByActive(Boolean active, Pageable pageable);
/**
* 根据年龄范围查找用户
*/
List<User> findByAgeBetween(Integer minAge, Integer maxAge);
/**
* 根据用户名模糊查找(忽略大小写)
*/
List<User> findByUsernameContainingIgnoreCase(String username);
/**
* 根据邮箱域名查找用户
*/
@Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
List<User> findByEmailDomain(@Param("domain") String domain);
/**
* 查找指定时间之后创建的用户
*/
@Query("SELECT u FROM User u WHERE u.createdAt >= :date")
List<User> findUsersCreatedAfter(@Param("date") LocalDateTime date);
/**
* 统计激活用户数量
*/
@Query("SELECT COUNT(u) FROM User u WHERE u.active = true")
long countActiveUsers();
/**
* 查找最近注册的用户
*/
@Query("SELECT u FROM User u ORDER BY u.createdAt DESC")
List<User> findRecentUsers(Pageable pageable);
/**
* 根据多个条件查找用户
*/
@Query("SELECT u FROM User u WHERE " +
"(:username IS NULL OR u.username LIKE %:username%) AND " +
"(:email IS NULL OR u.email LIKE %:email%) AND " +
"(:active IS NULL OR u.active = :active) AND " +
"(:minAge IS NULL OR u.age >= :minAge) AND " +
"(:maxAge IS NULL OR u.age <= :maxAge)")
Page<User> findUsersByCriteria(
@Param("username") String username,
@Param("email") String email,
@Param("active") Boolean active,
@Param("minAge") Integer minAge,
@Param("maxAge") Integer maxAge,
Pageable pageable
);
}
1.6 服务层
// src/main/java/com/example/demo/service/UserService.java
package com.example.demo.service;
import com.example.demo.dto.CreateUserRequest;
import com.example.demo.dto.UserDto;
import com.example.demo.model.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
/**
* 用户服务接口
*
* 定义用户相关的业务操作
*/
public interface UserService {
/**
* 创建用户
*/
UserDto createUser(CreateUserRequest request);
/**
* 根据 ID 查找用户
*/
Optional<UserDto> findById(Long id);
/**
* 根据用户名查找用户
*/
Optional<UserDto> findByUsername(String username);
/**
* 根据邮箱查找用户
*/
Optional<UserDto> findByEmail(String email);
/**
* 获取所有用户
*/
List<UserDto> findAll();
/**
* 分页获取用户
*/
Page<UserDto> findAll(Pageable pageable);
/**
* 根据激活状态获取用户
*/
List<UserDto> findByActive(Boolean active);
/**
* 根据激活状态分页获取用户
*/
Page<UserDto> findByActive(Boolean active, Pageable pageable);
/**
* 更新用户信息
*/
UserDto updateUser(Long id, UserDto userDto);
/**
* 激活用户
*/
void activateUser(Long id);
/**
* 停用用户
*/
void deactivateUser(Long id);
/**
* 删除用户
*/
void deleteUser(Long id);
/**
* 检查用户名是否存在
*/
boolean existsByUsername(String username);
/**
* 检查邮箱是否存在
*/
boolean existsByEmail(String email);
/**
* 根据年龄范围查找用户
*/
List<UserDto> findByAgeRange(Integer minAge, Integer maxAge);
/**
* 搜索用户
*/
List<UserDto> searchUsers(String keyword);
/**
* 根据条件查找用户
*/
Page<UserDto> findUsersByCriteria(String username, String email, Boolean active,
Integer minAge, Integer maxAge, Pageable pageable);
/**
* 获取用户统计信息
*/
UserStatistics getUserStatistics();
/**
* 用户统计信息内部类
*/
class UserStatistics {
private final long totalUsers;
private final long activeUsers;
private final long inactiveUsers;
private final LocalDateTime lastRegistration;
public UserStatistics(long totalUsers, long activeUsers, long inactiveUsers, LocalDateTime lastRegistration) {
this.totalUsers = totalUsers;
this.activeUsers = activeUsers;
this.inactiveUsers = inactiveUsers;
this.lastRegistration = lastRegistration;
}
public long getTotalUsers() { return totalUsers; }
public long getActiveUsers() { return activeUsers; }
public long getInactiveUsers() { return inactiveUsers; }
public LocalDateTime getLastRegistration() { return lastRegistration; }
@Override
public String toString() {
return "UserStatistics{" +
"totalUsers=" + totalUsers +
", activeUsers=" + activeUsers +
", inactiveUsers=" + inactiveUsers +
", lastRegistration=" + lastRegistration +
'}';
}
}
}
// src/main/java/com/example/demo/service/UserServiceImpl.java
package com.example.demo.service;
import com.example.demo.dto.CreateUserRequest;
import com.example.demo.dto.UserDto;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 用户服务实现类
*
* 实现用户相关的业务逻辑
*/
@Service
@Transactional
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDto createUser(CreateUserRequest request) {
// 检查用户名是否已存在
if (userRepository.existsByUsername(request.getUsername())) {
throw new IllegalArgumentException("用户名已存在: " + request.getUsername());
}
// 检查邮箱是否已存在
if (userRepository.existsByEmail(request.getEmail())) {
throw new IllegalArgumentException("邮箱已存在: " + request.getEmail());
}
// 创建用户实体
User user = new User(
request.getUsername(),
request.getEmail(),
request.getPassword(),
request.getFullName(),
request.getAge()
);
// 保存用户
User savedUser = userRepository.save(user);
// 转换为 DTO 并返回
return convertToDto(savedUser);
}
@Override
@Transactional(readOnly = true)
public Optional<UserDto> findById(Long id) {
return userRepository.findById(id)
.map(this::convertToDto);
}
@Override
@Transactional(readOnly = true)
public Optional<UserDto> findByUsername(String username) {
return userRepository.findByUsername(username)
.map(this::convertToDto);
}
@Override
@Transactional(readOnly = true)
public Optional<UserDto> findByEmail(String email) {
return userRepository.findByEmail(email)
.map(this::convertToDto);
}
@Override
@Transactional(readOnly = true)
public List<UserDto> findAll() {
return userRepository.findAll().stream()
.map(this::convertToDto)
.collect(Collectors.toList());
}
@Override
@Transactional(readOnly = true)
public Page<UserDto> findAll(Pageable pageable) {
return userRepository.findAll(pageable)
.map(this::convertToDto);
}
@Override
@Transactional(readOnly = true)
public List<UserDto> findByActive(Boolean active) {
return userRepository.findByActive(active).stream()
.map(this::convertToDto)
.collect(Collectors.toList());
}
@Override
@Transactional(readOnly = true)
public Page<UserDto> findByActive(Boolean active, Pageable pageable) {
return userRepository.findByActive(active, pageable)
.map(this::convertToDto);
}
@Override
public UserDto updateUser(Long id, UserDto userDto) {
User user = userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("用户不存在: " + id));
// 更新用户信息
if (userDto.getUsername() != null && !userDto.getUsername().equals(user.getUsername())) {
if (userRepository.existsByUsername(userDto.getUsername())) {
throw new IllegalArgumentException("用户名已存在: " + userDto.getUsername());
}
user.setUsername(userDto.getUsername());
}
if (userDto.getEmail() != null && !userDto.getEmail().equals(user.getEmail())) {
if (userRepository.existsByEmail(userDto.getEmail())) {
throw new IllegalArgumentException("邮箱已存在: " + userDto.getEmail());
}
user.setEmail(userDto.getEmail());
}
if (userDto.getFullName() != null) {
user.setFullName(userDto.getFullName());
}
if (userDto.getAge() != null) {
user.setAge(userDto.getAge());
}
if (userDto.getActive() != null) {
user.setActive(userDto.getActive());
}
User updatedUser = userRepository.save(user);
return convertToDto(updatedUser);
}
@Override
public void activateUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("用户不存在: " + id));
user.activate();
userRepository.save(user);
}
@Override
public void deactivateUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("用户不存在: " + id));
user.deactivate();
userRepository.save(user);
}
@Override
public void deleteUser(Long id) {
if (!userRepository.existsById(id)) {
throw new IllegalArgumentException("用户不存在: " + id);
}
userRepository.deleteById(id);
}
@Override
@Transactional(readOnly = true)
public boolean existsByUsername(String username) {
return userRepository.existsByUsername(username);
}
@Override
@Transactional(readOnly = true)
public boolean existsByEmail(String email) {
return userRepository.existsByEmail(email);
}
@Override
@Transactional(readOnly = true)
public List<UserDto> findByAgeRange(Integer minAge, Integer maxAge) {
return userRepository.findByAgeBetween(minAge, maxAge).stream()
.map(this::convertToDto)
.collect(Collectors.toList());
}
@Override
@Transactional(readOnly = true)
public List<UserDto> searchUsers(String keyword) {
return userRepository.findByUsernameContainingIgnoreCase(keyword).stream()
.map(this::convertToDto)
.collect(Collectors.toList());
}
@Override
@Transactional(readOnly = true)
public Page<UserDto> findUsersByCriteria(String username, String email, Boolean active,
Integer minAge, Integer maxAge, Pageable pageable) {
return userRepository.findUsersByCriteria(username, email, active, minAge, maxAge, pageable)
.map(this::convertToDto);
}
@Override
@Transactional(readOnly = true)
public UserStatistics getUserStatistics() {
long totalUsers = userRepository.count();
long activeUsers = userRepository.countActiveUsers();
long inactiveUsers = totalUsers - activeUsers;
LocalDateTime lastRegistration = null;
List<User> recentUsers = userRepository.findRecentUsers(PageRequest.of(0, 1));
if (!recentUsers.isEmpty()) {
lastRegistration = recentUsers.get(0).getCreatedAt();
}
return new UserStatistics(totalUsers, activeUsers, inactiveUsers, lastRegistration);
}
/**
* 将 User 实体转换为 UserDto
*/
private UserDto convertToDto(User user) {
return new UserDto(
user.getId(),
user.getUsername(),
user.getEmail(),
user.getFullName(),
user.getAge(),
user.getActive(),
user.getCreatedAt(),
user.getUpdatedAt()
);
}
}
2. 控制器层
2.1 用户控制器
// src/main/java/com/example/demo/controller/UserController.java
package com.example.demo.controller;
import com.example.demo.dto.CreateUserRequest;
import com.example.demo.dto.UserDto;
import com.example.demo.service.UserService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* 用户控制器
*
* 提供用户相关的 REST API 接口
*/
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 创建用户
*/
@PostMapping
public ResponseEntity<UserDto> createUser(@Valid @RequestBody CreateUserRequest request) {
try {
UserDto user = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
}
}
/**
* 根据 ID 获取用户
*/
@GetMapping("/{id}")
public ResponseEntity<UserDto> getUserById(@PathVariable @Min(1) Long id) {
Optional<UserDto> user = userService.findById(id);
return user.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 获取所有用户(分页)
*/
@GetMapping
public ResponseEntity<Page<UserDto>> getAllUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "asc") String sortDir) {
Sort sort = sortDir.equalsIgnoreCase("desc")
? Sort.by(sortBy).descending()
: Sort.by(sortBy).ascending();
Pageable pageable = PageRequest.of(page, size, sort);
Page<UserDto> users = userService.findAll(pageable);
return ResponseEntity.ok(users);
}
/**
* 根据激活状态获取用户
*/
@GetMapping("/active/{active}")
public ResponseEntity<List<UserDto>> getUsersByActive(@PathVariable Boolean active) {
List<UserDto> users = userService.findByActive(active);
return ResponseEntity.ok(users);
}
/**
* 搜索用户
*/
@GetMapping("/search")
public ResponseEntity<Page<UserDto>> searchUsers(
@RequestParam(required = false) String username,
@RequestParam(required = false) String email,
@RequestParam(required = false) Boolean active,
@RequestParam(required = false) Integer minAge,
@RequestParam(required = false) Integer maxAge,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "asc") String sortDir) {
Sort sort = sortDir.equalsIgnoreCase("desc")
? Sort.by(sortBy).descending()
: Sort.by(sortBy).ascending();
Pageable pageable = PageRequest.of(page, size, sort);
Page<UserDto> users = userService.findUsersByCriteria(
username, email, active, minAge, maxAge, pageable);
return ResponseEntity.ok(users);
}
/**
* 根据关键词搜索用户
*/
@GetMapping("/search/{keyword}")
public ResponseEntity<List<UserDto>> searchUsersByKeyword(@PathVariable String keyword) {
List<UserDto> users = userService.searchUsers(keyword);
return ResponseEntity.ok(users);
}
/**
* 更新用户信息
*/
@PutMapping("/{id}")
public ResponseEntity<UserDto> updateUser(
@PathVariable @Min(1) Long id,
@Valid @RequestBody UserDto userDto) {
try {
UserDto updatedUser = userService.updateUser(id, userDto);
return ResponseEntity.ok(updatedUser);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
}
}
/**
* 激活用户
*/
@PatchMapping("/{id}/activate")
public ResponseEntity<Void> activateUser(@PathVariable @Min(1) Long id) {
try {
userService.activateUser(id);
return ResponseEntity.ok().build();
} catch (IllegalArgumentException e) {
return ResponseEntity.notFound().build();
}
}
/**
* 停用用户
*/
@PatchMapping("/{id}/deactivate")
public ResponseEntity<Void> deactivateUser(@PathVariable @Min(1) Long id) {
try {
userService.deactivateUser(id);
return ResponseEntity.ok().build();
} catch (IllegalArgumentException e) {
return ResponseEntity.notFound().build();
}
}
/**
* 删除用户
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable @Min(1) Long id) {
try {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
} catch (IllegalArgumentException e) {
return ResponseEntity.notFound().build();
}
}
/**
* 检查用户名是否存在
*/
@GetMapping("/check/username/{username}")
public ResponseEntity<Map<String, Boolean>> checkUsername(@PathVariable String username) {
boolean exists = userService.existsByUsername(username);
return ResponseEntity.ok(Map.of("exists", exists));
}
/**
* 检查邮箱是否存在
*/
@GetMapping("/check/email/{email}")
public ResponseEntity<Map<String, Boolean>> checkEmail(@PathVariable String email) {
boolean exists = userService.existsByEmail(email);
return ResponseEntity.ok(Map.of("exists", exists));
}
/**
* 获取用户统计信息
*/
@GetMapping("/statistics")
public ResponseEntity<UserService.UserStatistics> getUserStatistics() {
UserService.UserStatistics statistics = userService.getUserStatistics();
return ResponseEntity.ok(statistics);
}
/**
* 根据年龄范围获取用户
*/
@GetMapping("/age-range")
public ResponseEntity<List<UserDto>> getUsersByAgeRange(
@RequestParam Integer minAge,
@RequestParam Integer maxAge) {
List<UserDto> users = userService.findByAgeRange(minAge, maxAge);
return ResponseEntity.ok(users);
}
}
2.2 健康检查控制器
// src/main/java/com/example/demo/controller/HealthController.java
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuator.health.Health;
import org.springframework.boot.actuator.health.HealthIndicator;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
import java.sql.Connection;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* 健康检查控制器
*
* 提供应用健康状态检查接口
*/
@RestController
@RequestMapping("/api/health")
public class HealthController implements HealthIndicator {
@Autowired
private DataSource dataSource;
/**
* 基本健康检查
*/
@GetMapping
public ResponseEntity<Map<String, Object>> health() {
Map<String, Object> health = new HashMap<>();
health.put("status", "UP");
health.put("timestamp", LocalDateTime.now());
health.put("application", "Spring Native Demo");
health.put("version", "1.0.0");
return ResponseEntity.ok(health);
}
/**
* 详细健康检查
*/
@GetMapping("/detailed")
public ResponseEntity<Map<String, Object>> detailedHealth() {
Map<String, Object> health = new HashMap<>();
// 基本信息
health.put("status", "UP");
health.put("timestamp", LocalDateTime.now());
health.put("application", "Spring Native Demo");
health.put("version", "1.0.0");
// 系统信息
Map<String, Object> system = new HashMap<>();
Runtime runtime = Runtime.getRuntime();
system.put("processors", runtime.availableProcessors());
system.put("totalMemory", runtime.totalMemory());
system.put("freeMemory", runtime.freeMemory());
system.put("maxMemory", runtime.maxMemory());
system.put("usedMemory", runtime.totalMemory() - runtime.freeMemory());
health.put("system", system);
// 数据库连接检查
Map<String, Object> database = new HashMap<>();
try (Connection connection = dataSource.getConnection()) {
database.put("status", "UP");
database.put("url", connection.getMetaData().getURL());
database.put("driver", connection.getMetaData().getDriverName());
database.put("version", connection.getMetaData().getDriverVersion());
} catch (Exception e) {
database.put("status", "DOWN");
database.put("error", e.getMessage());
}
health.put("database", database);
return ResponseEntity.ok(health);
}
/**
* Spring Boot Actuator 健康指示器
*/
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
return Health.up()
.withDetail("database", "Available")
.withDetail("timestamp", LocalDateTime.now())
.build();
} catch (Exception e) {
return Health.down()
.withDetail("database", "Unavailable")
.withDetail("error", e.getMessage())
.withDetail("timestamp", LocalDateTime.now())
.build();
}
}
/**
* 就绪检查
*/
@GetMapping("/ready")
public ResponseEntity<Map<String, Object>> ready() {
Map<String, Object> readiness = new HashMap<>();
boolean isReady = true;
StringBuilder message = new StringBuilder();
// 检查数据库连接
try (Connection connection = dataSource.getConnection()) {
message.append("Database: OK; ");
} catch (Exception e) {
isReady = false;
message.append("Database: FAIL - ").append(e.getMessage()).append("; ");
}
readiness.put("ready", isReady);
readiness.put("status", isReady ? "READY" : "NOT_READY");
readiness.put("message", message.toString());
readiness.put("timestamp", LocalDateTime.now());
return ResponseEntity.ok(readiness);
}
/**
* 存活检查
*/
@GetMapping("/live")
public ResponseEntity<Map<String, Object>> live() {
Map<String, Object> liveness = new HashMap<>();
liveness.put("alive", true);
liveness.put("status", "ALIVE");
liveness.put("timestamp", LocalDateTime.now());
liveness.put("uptime", System.currentTimeMillis());
return ResponseEntity.ok(liveness);
}
}
3. 配置类
3.1 Web 配置
// src/main/java/com/example/demo/config/WebConfig.java
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web 配置类
*
* 配置 Web 相关设置,如 CORS、拦截器等
*/
@Configuration
@EnableJpaAuditing
public class WebConfig implements WebMvcConfigurer {
/**
* 配置 CORS
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000", "http://localhost:8080")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
3.2 原生配置
// src/main/java/com/example/demo/config/NativeConfig.java
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.nativex.hint.NativeHint;
import org.springframework.nativex.hint.TypeHint;
import org.springframework.nativex.hint.TypeAccess;
import org.springframework.nativex.hint.ResourceHint;
import org.springframework.nativex.hint.JniHint;
/**
* 原生镜像配置类
*
* 提供原生编译所需的额外配置信息
*/
@Configuration
@NativeHint(
// 反射类型配置
types = {
// H2 数据库相关
@TypeHint(
types = {
org.h2.Driver.class,
org.h2.engine.Engine.class,
org.h2.store.fs.FilePathDisk.class
},
access = {
TypeAccess.DECLARED_CONSTRUCTORS,
TypeAccess.DECLARED_METHODS
}
),
// Hibernate 相关
@TypeHint(
types = {
org.hibernate.dialect.H2Dialect.class,
org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl.class,
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl.class
},
access = TypeAccess.DECLARED_CONSTRUCTORS
),
// Jackson 相关
@TypeHint(
types = {
com.fasterxml.jackson.databind.ser.std.StringSerializer.class,
com.fasterxml.jackson.databind.ser.std.NumberSerializers.LongSerializer.class,
com.fasterxml.jackson.databind.ser.std.NumberSerializers.IntegerSerializer.class,
com.fasterxml.jackson.databind.ser.std.BooleanSerializer.class,
com.fasterxml.jackson.databind.deser.std.StringDeserializer.class,
com.fasterxml.jackson.databind.deser.std.NumberDeserializers.LongDeserializer.class,
com.fasterxml.jackson.databind.deser.std.NumberDeserializers.IntegerDeserializer.class,
com.fasterxml.jackson.databind.deser.std.NumberDeserializers.BooleanDeserializer.class
},
access = TypeAccess.DECLARED_CONSTRUCTORS
),
// 应用实体类
@TypeHint(
types = {
com.example.demo.model.User.class,
com.example.demo.dto.UserDto.class,
com.example.demo.dto.CreateUserRequest.class
},
access = {
TypeAccess.DECLARED_CONSTRUCTORS,
TypeAccess.DECLARED_METHODS,
TypeAccess.DECLARED_FIELDS,
TypeAccess.PUBLIC_METHODS
}
)
},
// 资源配置
resources = {
@ResourceHint(patterns = {
"application*.yml",
"application*.yaml",
"application*.properties",
"META-INF/spring.factories",
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
"org/hibernate/validator/ValidationMessages.properties",
"ValidationMessages.properties"
})
}
)
public class NativeConfig {
// 配置类主体为空,所有配置通过注解提供
}
4. 静态资源
4.1 首页
<!-- src/main/resources/static/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spring Native Demo</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
padding: 40px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
color: #4a5568;
margin-bottom: 40px;
font-size: 2.5em;
font-weight: 300;
}
.status-card {
background: linear-gradient(135deg, #48bb78 0%, #38a169 100%);
color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
margin-bottom: 30px;
box-shadow: 0 10px 20px rgba(72, 187, 120, 0.3);
}
.api-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 25px;
margin-bottom: 30px;
}
.api-section {
background: #f7fafc;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 25px;
transition: transform 0.2s, box-shadow 0.2s;
}
.api-section:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}
.api-title {
font-size: 1.3em;
font-weight: 600;
color: #2d3748;
margin-bottom: 15px;
border-bottom: 2px solid #667eea;
padding-bottom: 10px;
}
.endpoint {
background: #edf2f7;
border-left: 4px solid #667eea;
padding: 12px 15px;
margin: 10px 0;
border-radius: 5px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
transition: background 0.2s;
}
.endpoint:hover {
background: #e2e8f0;
}
.method {
font-weight: bold;
color: #667eea;
}
.footer {
text-align: center;
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #e2e8f0;
color: #718096;
}
.build-info {
background: #f0fff4;
border: 1px solid #9ae6b4;
border-radius: 8px;
padding: 15px;
margin-top: 20px;
}
@media (max-width: 768px) {
.container {
padding: 20px;
}
.api-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Spring Native Demo Application</h1>
<div class="status-card">
<h2>✅ 应用运行状态</h2>
<p id="status-text">应用正常运行中</p>
<p>启动模式: <span id="runtime-mode">检测中...</span></p>
</div>
<div class="api-grid">
<div class="api-section">
<div class="api-title">👥 用户管理 API</div>
<div class="endpoint">
<span class="method">GET</span> /api/users - 获取所有用户
</div>
<div class="endpoint">
<span class="method">GET</span> /api/users/{id} - 获取指定用户
</div>
<div class="endpoint">
<span class="method">POST</span> /api/users - 创建新用户
</div>
<div class="endpoint">
<span class="method">PUT</span> /api/users/{id} - 更新用户信息
</div>
<div class="endpoint">
<span class="method">DELETE</span> /api/users/{id} - 删除用户
</div>
</div>
<div class="api-section">
<div class="api-title">🏥 健康检查</div>
<div class="endpoint">
<span class="method">GET</span> /health - 基础健康状态
</div>
<div class="endpoint">
<span class="method">GET</span> /health/detailed - 详细健康信息
</div>
</div>
<div class="api-section">
<div class="api-title">📊 统计信息</div>
<div class="endpoint">
<span class="method">GET</span> /api/users/stats - 用户统计数据
</div>
</div>
<div class="api-section">
<div class="api-title">🔧 系统信息</div>
<div class="endpoint">
<span class="method">GET</span> /actuator/info - 应用信息
</div>
<div class="endpoint">
<span class="method">GET</span> /actuator/metrics - 性能指标
</div>
</div>
</div>
<div class="footer">
<h3>Spring Native Demo</h3>
<p>原生镜像应用示例 - 展示 Spring Native 的强大性能</p>
<div class="build-info">
<strong>构建信息:</strong><br>
构建时间: <span id="build-time"></span><br>
运行时间: <span id="uptime"></span>
</div>
</div>
</div>
<script>
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
updateBuildTime();
checkApplicationHealth();
detectRuntimeMode();
startUptimeCounter();
});
// 更新构建时间
function updateBuildTime() {
document.getElementById('build-time').textContent = new Date().toLocaleString('zh-CN');
}
// 检查应用健康状态
async function checkApplicationHealth() {
try {
const response = await fetch('/health');
const statusText = document.getElementById('status-text');
if (response.ok) {
statusText.textContent = '✅ 应用健康运行中';
statusText.style.color = '#48bb78';
} else {
statusText.textContent = '⚠️ 应用状态异常';
statusText.style.color = '#f56565';
}
} catch (error) {
document.getElementById('status-text').textContent = '❌ 无法连接到应用';
console.error('健康检查失败:', error);
}
}
// 检测运行时模式
async function detectRuntimeMode() {
try {
const response = await fetch('/health/detailed');
if (response.ok) {
const data = await response.json();
const mode = data.nativeImage ? 'Native Image' : 'JVM';
document.getElementById('runtime-mode').textContent = mode;
}
} catch (error) {
document.getElementById('runtime-mode').textContent = '未知';
}
}
// 启动运行时间计数器
function startUptimeCounter() {
const startTime = Date.now();
setInterval(() => {
const uptime = Date.now() - startTime;
const seconds = Math.floor(uptime / 1000) % 60;
const minutes = Math.floor(uptime / (1000 * 60)) % 60;
const hours = Math.floor(uptime / (1000 * 60 * 60));
document.getElementById('uptime').textContent =
`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
}, 1000);
}
// 定期健康检查
setInterval(checkApplicationHealth, 30000); // 每30秒检查一次
</script>
</body>
</html>
5. 构建与运行
5.1 传统 JVM 模式运行
# 编译项目
mvn clean compile
# 运行应用
mvn spring-boot:run
# 或者打包后运行
mvn clean package
java -jar target/spring-native-demo-1.0.0.jar
5.2 原生镜像构建
# 构建原生镜像(需要 GraalVM)
mvn clean -Pnative native:compile
# 运行原生镜像
./target/spring-native-demo
# Windows 环境
.\target\spring-native-demo.exe
5.3 Docker 构建
# Dockerfile.native
FROM ghcr.io/graalvm/graalvm-ce:ol8-java17-22.3.0 AS builder
WORKDIR /app
COPY . .
# 安装 Maven
RUN microdnf install -y wget tar gzip && \
wget https://archive.apache.org/dist/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz && \
tar -xzf apache-maven-3.8.6-bin.tar.gz && \
mv apache-maven-3.8.6 /opt/maven && \
ln -s /opt/maven/bin/mvn /usr/local/bin/mvn
# 构建原生镜像
RUN mvn clean -Pnative native:compile
# 运行时镜像
FROM oraclelinux:8-slim
WORKDIR /app
COPY --from=builder /app/target/spring-native-demo ./app
EXPOSE 8080
CMD ["./app"]
# 构建 Docker 镜像
docker build -f Dockerfile.native -t spring-native-demo:native .
# 运行容器
docker run -p 8080:8080 spring-native-demo:native
6. 性能对比测试
6.1 启动时间测试
#!/bin/bash
# startup-test.sh
echo "=== Spring Native 启动时间测试 ==="
# JVM 模式测试
echo "测试 JVM 模式启动时间..."
JVM_START=$(date +%s%N)
java -jar target/spring-native-demo-1.0.0.jar &
JVM_PID=$!
# 等待应用启动
while ! curl -s http://localhost:8080/health > /dev/null; do
sleep 0.1
done
JVM_END=$(date +%s%N)
JVM_TIME=$(((JVM_END - JVM_START) / 1000000))
echo "JVM 模式启动时间: ${JVM_TIME}ms"
# 停止 JVM 应用
kill $JVM_PID
sleep 2
# 原生镜像测试
echo "测试原生镜像启动时间..."
NATIVE_START=$(date +%s%N)
./target/spring-native-demo &
NATIVE_PID=$!
# 等待应用启动
while ! curl -s http://localhost:8080/health > /dev/null; do
sleep 0.1
done
NATIVE_END=$(date +%s%N)
NATIVE_TIME=$(((NATIVE_END - NATIVE_START) / 1000000))
echo "原生镜像启动时间: ${NATIVE_TIME}ms"
# 计算性能提升
IMPROVEMENT=$((JVM_TIME / NATIVE_TIME))
echo "性能提升: ${IMPROVEMENT}x"
# 停止原生应用
kill $NATIVE_PID
6.2 内存使用测试
#!/bin/bash
# memory-test.sh
echo "=== Spring Native 内存使用测试 ==="
# JVM 模式内存测试
echo "启动 JVM 模式应用..."
java -jar target/spring-native-demo-1.0.0.jar &
JVM_PID=$!
sleep 10
JVM_MEMORY=$(ps -o rss= -p $JVM_PID)
echo "JVM 模式内存使用: ${JVM_MEMORY}KB"
kill $JVM_PID
sleep 2
# 原生镜像内存测试
echo "启动原生镜像应用..."
./target/spring-native-demo &
NATIVE_PID=$!
sleep 10
NATIVE_MEMORY=$(ps -o rss= -p $NATIVE_PID)
echo "原生镜像内存使用: ${NATIVE_MEMORY}KB"
# 计算内存节省
MEMORY_SAVING=$(((JVM_MEMORY - NATIVE_MEMORY) * 100 / JVM_MEMORY))
echo "内存节省: ${MEMORY_SAVING}%"
kill $NATIVE_PID
7. 测试验证
7.1 API 功能测试
#!/bin/bash
# api-test.sh
BASE_URL="http://localhost:8080"
echo "=== Spring Native API 功能测试 ==="
# 健康检查
echo "1. 健康检查测试"
curl -s "$BASE_URL/health" | jq .
# 创建用户
echo "2. 创建用户测试"
USER_DATA='{"name":"张三","email":"zhangsan@example.com","age":25}'
USER_ID=$(curl -s -X POST "$BASE_URL/api/users" \
-H "Content-Type: application/json" \
-d "$USER_DATA" | jq -r '.id')
echo "创建用户 ID: $USER_ID"
# 查询用户
echo "3. 查询用户测试"
curl -s "$BASE_URL/api/users/$USER_ID" | jq .
# 更新用户
echo "4. 更新用户测试"
UPDATE_DATA='{"name":"张三丰","email":"zhangsanfeng@example.com","age":30}'
curl -s -X PUT "$BASE_URL/api/users/$USER_ID" \
-H "Content-Type: application/json" \
-d "$UPDATE_DATA" | jq .
# 获取所有用户
echo "5. 获取所有用户测试"
curl -s "$BASE_URL/api/users" | jq .
# 获取统计信息
echo "6. 获取统计信息测试"
curl -s "$BASE_URL/api/users/stats" | jq .
# 删除用户
echo "7. 删除用户测试"
curl -s -X DELETE "$BASE_URL/api/users/$USER_ID"
echo "用户已删除"
echo "API 功能测试完成!"
7.2 压力测试
#!/bin/bash
# load-test.sh
echo "=== Spring Native 压力测试 ==="
# 使用 Apache Bench 进行压力测试
echo "开始压力测试(1000 请求,并发 10)..."
ab -n 1000 -c 10 http://localhost:8080/health
echo "开始 API 压力测试(500 请求,并发 5)..."
ab -n 500 -c 5 -H "Content-Type: application/json" \
-p user-data.json http://localhost:8080/api/users
8. 核心要点
8.1 架构设计
- 分层架构:采用经典的 MVC 分层架构,便于维护和扩展
- 依赖注入:充分利用 Spring 的 IoC 容器管理组件依赖
- 配置管理:使用
@ConfigurationProperties
实现类型安全的配置 - 异常处理:统一的异常处理机制,提供友好的错误响应
8.2 最佳实践
- 原生提示:使用
@NativeHint
注解提供编译时提示 - 资源管理:合理配置静态资源和反射配置
- 性能优化:避免使用动态特性,优先使用编译时确定的代码
- 测试覆盖:确保核心功能有完整的测试覆盖
8.3 开发注意事项
- 反射限制:避免使用运行时反射,使用编译时代码生成
- 动态代理:谨慎使用动态代理,优先使用接口代理
- 类路径扫描:避免运行时类路径扫描,使用显式配置
- 序列化:确保序列化类在编译时可见
9. 下一步学习
- 原生镜像构建:学习 GraalVM 原生镜像构建过程和优化技巧
- 配置优化:深入了解原生镜像配置文件的编写和调优
- 性能调优:掌握原生镜像的性能分析和优化方法
- 部署实践:学习原生镜像在容器化环境中的部署策略
通过本章的学习,你已经掌握了 Spring Native 基础应用的开发方法。接下来我们将学习如何构建和优化原生镜像。