Spring Boot Coding Standards
Follow these coding standards when developing Spring Boot applications.
Constructor Injection (Required)
ALWAYS use constructor injection instead of field injection:
// ✅ CORRECT: Constructor injection
@Service
public class AppointmentService {
private final AppointmentRepository appointmentRepository;
private final CustomerService customerService;
public AppointmentService(AppointmentRepository appointmentRepository,
CustomerService customerService) {
this.appointmentRepository = appointmentRepository;
this.customerService = customerService;
}
}
// ❌ WRONG: Field injection
@Service
public class AppointmentService {
@Autowired
private AppointmentRepository appointmentRepository;
@Autowired
private CustomerService customerService;
}
DTOs and Value Objects
Create immutable DTOs when possible:
// Request DTO with validation
public class AppointmentRequestDTO {
@NotNull(message = "Customer ID is required")
private Long customerId;
@NotNull(message = "Employee ID is required")
private Long employeeId;
@NotNull(message = "Service type is required")
private Long serviceTypeId;
@FutureOrPresent(message = "Appointment time must be in the future")
private LocalDateTime appointmentTime;
// Getters and setters
}
// Response DTO (immutable pattern)
public final class AppointmentResponseDTO {
private final Long id;
private final String customerName;
private final String employeeName;
private final String serviceType;
private final LocalDateTime appointmentTime;
private final String status;
// Constructor and getters only
}
Error Handling
Implement global exception handling:
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
logger.error("Resource not found: {}", ex.getMessage());
return new ResponseEntity<>(
new ErrorResponse("NOT_FOUND", ex.getMessage()),
HttpStatus.NOT_FOUND
);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
String message = ex.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return new ResponseEntity<>(
new ErrorResponse("VALIDATION_ERROR", message),
HttpStatus.BAD_REQUEST
);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneric(Exception ex) {
logger.error("Unexpected error", ex);
return new ResponseEntity<>(
new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred"),
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}
Optional Handling
Never use .get() without checking:
// ✅ CORRECT
public User getUser(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found: " + id));
}
// ❌ WRONG - Can throw NoSuchElementException
public User getUser(Long id) {
return userRepository.findById(id).get();
}
RESTful API Design
Follow REST conventions:
@RestController
@RequestMapping("/api/appointments")
public class AppointmentController {
@GetMapping // GET all
public List<AppointmentDTO> getAll() { }
@GetMapping("/{id}") // GET by ID
public AppointmentDTO getById(@PathVariable Long id) { }
@PostMapping // CREATE
public ResponseEntity<AppointmentDTO> create(@Valid @RequestBody AppointmentRequestDTO dto) {
AppointmentDTO created = service.create(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@PutMapping("/{id}") // UPDATE
public AppointmentDTO update(@PathVariable Long id, @Valid @RequestBody AppointmentRequestDTO dto) { }
@DeleteMapping("/{id}") // DELETE
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}
Lombok Usage
Use Lombok judiciously:
@Data // For entities with caution (equals/hashCode issues)
@NoArgsConstructor // Required for JPA
@AllArgsConstructor // Useful for testing
@Builder // For complex object construction
public class Appointment {
// fields
}
// For DTOs, prefer explicit immutable pattern or:
@Getter
@AllArgsConstructor
public class AppointmentResponseDTO {
private final Long id;
private final String name;
}
Input Validation
Always validate user input:
@PostMapping
public ResponseEntity<CustomerDTO> createCustomer(
@Valid @RequestBody CustomerRequest request) {
// Process validated input
}
public class CustomerRequest {
@NotBlank(message = "Name is required")
@Size(min = 2, max = 100, message = "Name must be 2-100 characters")
private String name;
@Email(message = "Invalid email format")
private String email;
@Pattern(regexp = "^\\+?[1-9]\\d{1,14}$", message = "Invalid phone number")
private String phone;
}
Transaction Management
@Service
@Transactional(readOnly = true) // Default read-only for safety
public class OrderService {
@Transactional // Override for write operations
public Order placeOrder(OrderRequest request) {
Order order = new Order();
// ... set order details
return orderRepository.save(order);
}
// Read operation uses class-level readOnly = true
public Order getOrder(Long id) {
return orderRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Order not found"));
}
}
微信扫一扫