You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Spring Boot REST服务创建规范、模板及示例求助(新手开发者)

给Spring Boot REST API新手的入门指南

一、良好的开发习惯与规则

  • 严格分层架构:一定要按职责拆分代码,比如Controller(专门处理HTTP请求响应)、Service(封装业务逻辑)、Repository(数据访问操作)、Model/Entity(数据库映射模型),千万别把所有逻辑堆在一个类里,不然后期维护起来会头大。
  • 遵循RESTful规范:用对HTTP方法——GET查数据、POST新增、PUT全量更新、PATCH局部更新、DELETE删除;URL要语义化,比如/api/users获取所有用户,/api/users/{id}获取单个用户,别写/api/getUserById这种不符合规范的路径。
  • 全局异常处理:用@RestControllerAdvice配合@ExceptionHandler做统一异常拦截,返回包含错误码、提示信息、时间戳的标准化响应,别让前端看到一堆乱糟糟的栈追踪信息。
  • 参数校验前置:用JSR-380注解(比如@NotNull@Size@Email)配合@Valid在Controller层校验请求参数,把非法请求直接挡在门外,不用等到Service层再处理。
  • 规范日志输出:用SLF4J+Logback打印日志,别用System.out.println;日志分级别(DEBUG/INFO/WARN/ERROR),关键业务流程打INFO,错误场景打ERROR,调试信息打DEBUG,方便排查问题。

二、推荐的设计模式

  • MVC模式:这是Spring Boot REST项目的基础架构,Controller对应视图层(处理请求响应),Service对应控制层(业务逻辑),Repository对应模型层(数据访问),完美适配分层架构的思路。
  • DTO模式:别直接把数据库Entity返回给前端,用DTO(数据传输对象)封装需要对外暴露的字段——比如UserEntity里有密码,返回给前端的UserDTO就只放id、name、email,既避免敏感数据泄露,又能灵活控制返回内容。
  • 单例模式:Spring的Bean默认都是单例,不用自己实现,像Service、Repository这些无状态的类用单例刚好,节省资源。
  • 工厂模式:如果有多种业务实现(比如不同支付方式、不同消息推送渠道),可以用工厂模式创建对应的Service实例,避免写一堆if-else判断。

三、简单的REST API示例

1. 标准项目结构

src/main/java/com/example/demo
├── DemoApplication.java          // Spring Boot启动类
├── controller
│   └── UserController.java       // 用户接口控制器
├── service
│   ├── UserService.java          // 用户业务接口
│   └── impl
│       └── UserServiceImpl.java  // 用户业务实现类
├── repository
│   └── UserRepository.java       // 用户数据访问接口
├── model
│   ├── User.java                 // 用户实体类(映射数据库表)
│   └── dto
│       └── UserDTO.java          // 用户数据传输对象
└── exception
    ├── GlobalExceptionHandler.java // 全局异常处理类
    └── ResourceNotFoundException.java // 自定义资源不存在异常

2. 核心代码示例

实体类User.java

import jakarta.persistence.*;
import lombok.Data;

@Entity
@Table(name = "users")
@Data // Lombok注解,自动生成getter/setter、toString等方法
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private String name;
    @Column(nullable = false, unique = true)
    private String email;
    private String password; // 敏感字段,不对外暴露
}

DTO类UserDTO.java

import lombok.Data;

@Data
public class UserDTO {
    private Long id;
    private String name;
    private String email;
}

Repository接口UserRepository.java

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // JpaRepository已经提供了CRUD基础方法,无需手动实现
}

Service接口UserService.java

import com.example.demo.model.dto.UserDTO;
import java.util.List;

public interface UserService {
    List<UserDTO> getAllUsers();
    UserDTO getUserById(Long id);
    UserDTO createUser(User user);
    UserDTO updateUser(Long id, User userDetails);
    void deleteUser(Long id);
}

Service实现类UserServiceImpl.java

import com.example.demo.model.User;
import com.example.demo.model.dto.UserDTO;
import com.example.demo.repository.UserRepository;
import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.service.UserService;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ModelMapper modelMapper; // 需要引入modelmapper依赖简化对象转换

    @Override
    public List<UserDTO> getAllUsers() {
        return userRepository.findAll().stream()
                .map(user -> modelMapper.map(user, UserDTO.class))
                .collect(Collectors.toList());
    }

    @Override
    public UserDTO getUserById(Long id) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("用户不存在,ID:" + id));
        return modelMapper.map(user, UserDTO.class);
    }

    @Override
    public UserDTO createUser(User user) {
        User savedUser = userRepository.save(user);
        return modelMapper.map(savedUser, UserDTO.class);
    }

    @Override
    public UserDTO updateUser(Long id, User userDetails) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("用户不存在,ID:" + id));
        user.setName(userDetails.getName());
        user.setEmail(userDetails.getEmail());
        user.setPassword(userDetails.getPassword());
        User updatedUser = userRepository.save(user);
        return modelMapper.map(updatedUser, UserDTO.class);
    }

    @Override
    public void deleteUser(Long id) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("用户不存在,ID:" + id));
        userRepository.delete(user);
    }
}

Controller类UserController.java

import com.example.demo.model.User;
import com.example.demo.model.dto.UserDTO;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

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

    @Autowired
    private UserService userService;

    @GetMapping
    public List<UserDTO> getAllUsers() {
        return userService.getAllUsers();
    }

    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getUserById(@PathVariable Long id) {
        UserDTO userDTO = userService.getUserById(id);
        return ResponseEntity.ok(userDTO);
    }

    @PostMapping
    public ResponseEntity<UserDTO> createUser(@RequestBody User user) {
        UserDTO createdUser = userService.createUser(user);
        return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
    }

    @PutMapping("/{id}")
    public ResponseEntity<UserDTO> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
        UserDTO updatedUser = userService.updateUser(id, userDetails);
        return ResponseEntity.ok(updatedUser);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return ResponseEntity.noContent().build();
    }
}

自定义异常ResourceNotFoundException.java

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

全局异常处理GlobalExceptionHandler.java

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<Map<String, Object>> handleResourceNotFound(ResourceNotFoundException ex) {
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("timestamp", LocalDateTime.now());
        errorResponse.put("message", ex.getMessage());
        errorResponse.put("status", HttpStatus.NOT_FOUND.value());
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }

    // 可扩展其他异常处理,比如参数校验异常、全局未知异常等
}

四、测试案例示例

用Spring Boot Test + MockMvc编写单元测试,验证接口逻辑:

UserControllerTest.java

import com.example.demo.model.User;
import com.example.demo.model.dto.UserDTO;
import com.example.demo.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(UserController.class)
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    public void getAllUsers_ShouldReturnUserList() throws Exception {
        UserDTO user1 = new UserDTO();
        user1.setId(1L);
        user1.setName("Alice");
        user1.setEmail("alice@example.com");

        UserDTO user2 = new UserDTO();
        user2.setId(2L);
        user2.setName("Bob");
        user2.setEmail("bob@example.com");

        when(userService.getAllUsers()).thenReturn(Arrays.asList(user1, user2));

        mockMvc.perform(get("/api/users")
                        .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].name").value("Alice"))
                .andExpect(jsonPath("$[1].email").value("bob@example.com"));
    }

    @Test
    public void getUserById_ExistingId_ShouldReturnUser() throws Exception {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1L);
        userDTO.setName("Alice");
        userDTO.setEmail("alice@example.com");

        when(userService.getUserById(1L)).thenReturn(userDTO);

        mockMvc.perform(get("/api/users/1")
                        .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value("Alice"));
    }

    @Test
    public void createUser_ShouldReturnCreatedUser() throws Exception {
        User user = new User();
        user.setName("Charlie");
        user.setEmail("charlie@example.com");
        user.setPassword("123456");

        UserDTO createdUser = new UserDTO();
        createdUser.setId(3L);
        createdUser.setName("Charlie");
        createdUser.setEmail("charlie@example.com");

        when(userService.createUser(any(User.class))).thenReturn(createdUser);

        mockMvc.perform(post("/api/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(user)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.id").value(3L))
                .andExpect(jsonPath("$.name").value("Charlie"));
    }
}

五、额外小贴士

  • 用Lombok减少重复代码,@Data@NoArgsConstructor@AllArgsConstructor这些注解能省掉大量getter/setter和构造方法。
  • 引入ModelMapper简化Entity和DTO之间的转换,不用手动写一堆setter赋值。
  • 配置文件(application.yml/application.properties)里清晰配置数据库连接、日志级别、端口等信息,方便后期修改。
  • 提交代码前用SonarQube检查代码质量,提前发现潜在bug和不良编码习惯。

内容的提问来源于stack exchange,提问作者Magmaruss

火山引擎 最新活动