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




