Backend Development 8 min read

Avoid Common Spring Boot Mistakes and Write Cleaner Backend Code

This article highlights common pitfalls in Spring Boot development—such as exposing sensitive data, neglecting records, field injection, using RestTemplate, bloated controllers, and poor exception handling—and provides concise, code‑driven solutions to write cleaner, more secure, and maintainable backend applications.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Avoid Common Spring Boot Mistakes and Write Cleaner Backend Code

1. Introduction

Spring Boot is a lightweight framework built on Spring that simplifies application development by providing sensible defaults and ready‑to‑use tools, allowing developers to focus on business logic rather than extensive XML configuration.

2. Common Mistakes

2.1 Exposing Sensitive Data

Returning entity objects directly from controllers can unintentionally expose fields such as passwords, even when @JsonIgnore is used. The proper approach is to use DTOs or Java records.

@RestController
@RequestMapping("/users")
public class UserController {
    @Resource
    private UserRepository userRepository;

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userRepository.findById(id).orElseThrow();
    }
}

Use a DTO/record to transfer only the required data:

public record UserDTO(Long id, String name, String email) {}

2.2 Not Using Records

When using Java 14+ for immutable POJOs, prefer records over classic classes.

public class UserResponse {
    private final String username;
    private final Integer age;
    public UserResponse(String username, Integer age) {
        this.username = username;
        this.age = age;
    }
    // getters, setters
}

With a record the same can be expressed in a single line:

public record UserResponse(String username, Integer age) {}

2.3 Field Injection with @Autowired/@Resource

Using @Autowired or @Resource on fields is unnecessary and hinders testability. Constructor injection is preferred.

@Service
public class UserService {
    @Resource
    private UserRepository userRepository;
}

Correct constructor injection:

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

2.4 Using RestTemplate

RestTemplate is blocking and in maintenance mode. WebClient provides a non‑blocking, reactive alternative.

@Service
public class PaymentService {
    private final WebClient webClient;

    public PaymentService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://www.payment.com/api").build();
    }

    public Mono<PaymentResponse> processPayment(PaymentRequest request) {
        return webClient.post()
                .uri("/pay")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(PaymentResponse.class);
    }
}

Official recommendation:

WebClient recommendation
WebClient recommendation

2.5 Bloated Controllers

Putting business logic directly in controllers makes the code hard to maintain. Controllers should delegate to services.

@RestController
@RequestMapping("/users")
public class UserController {
    @Resource
    private UserRepository userRepository;

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userRepository.findById(id).orElseThrow();
    }
}

Correct separation:

@Service
public class UserService {
    private final UserRepository userRepository;

    public User getUser(Long id) {
        return userRepository.findById(id).orElseThrow();
    }
}

// Controller calls UserService

2.6 Ignoring Proper Exception Handling

Returning generic RuntimeException hides the real problem. Define custom exceptions and handle them globally.

@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
        super(message);
    }
}
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleNotFound(UserNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body("User not found, " + ex.getMessage());
    }
}
Javabackend developmentException HandlingDTOBest PracticesSpring BootWebClient
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.