Spring Boot Common Mistakes with Code Examples

Akın Topbaş
3 min readJul 18, 2024

--

Spring Boot Common Mistakes

Spring Boot has become a popular framework for developing Java applications due to its ease of use and powerful features. However, even experienced developers can fall into common pitfalls that can lead to inefficient code, difficult-to-maintain applications, and hidden bugs. In this article, we’ll explore some of the most common mistakes made with Spring Boot and how to avoid them, complete with code examples.

1. Not Using Spring Boot Starters

The Mistake

Spring Boot starters simplify dependency management by providing a convenient way to include a group of dependencies for a specific use case. A common mistake is manually specifying dependencies instead of using starters.

Example

Incorrect:

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<!-- more dependencies -->
</dependencies>

Correct:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- other starters as needed -->
</dependencies>

Explanation

Using Spring Boot starters helps manage versions and dependencies more effectively, reducing the risk of compatibility issues.

2. Ignoring Application Properties Configuration

The Mistake

Hardcoding configuration values directly in the code is a common mistake that makes the application less flexible and harder to manage.

Example

Incorrect:

@Service
public class MySampleService {
private final String url = "http://localhost:8080";
// ...
}

Correct:

# application.yml
myapp:
url: http://localhost:8080
@Service
public class MySampleService {
@Value("${myapp.url}")
private String url;
// ...
}

Explanation

Externalizing configuration values to application.properties or application.yml files makes the application easier to manage and adapt to different environments.

3. Misusing @Autowired Annotation

The Mistake

Using field injection (@Autowired on fields) instead of constructor injection can lead to issues, especially with testing and immutability.

Example

Incorrect:

@Service
public class MySampleService {
@Autowired
private MyRepository myRepository;
// ...
}

Correct:

@Service
public class MySampleService {
private final MyRepository myRepository;

// Also no need to this Autowired when you have one constructor.
@Autowired
public MySampleService(MyRepository myRepository) {
this.myRepository = myRepository;
}
// ...
}

Explanation

Constructor injection ensures that the required dependencies are available and allows for easier testing and immutability.

4. Not Using Profiles for Different Environments

The Mistake

Developers often forget to use Spring profiles to manage different configurations for various environments like development, testing, and production.

Example

Incorrect:

# application.yml
server:
port: 8080
# other configurations

Correct:

# application.yml
spring:
profiles:
active: dev

# application-dev.yml
server:
port: 8080

# application-prod.yml
server:
port: 80

Explanation

Using profiles allows for a clean separation of environment-specific configurations, making it easier to switch between environments.

5. Ignoring Actuator Endpoints

The Mistake

Spring Boot Actuator provides valuable endpoints for monitoring and managing the application, but they are often overlooked.

Example

Incorrect:

# application.yml
management:
endpoints:
web:
exposure:
include: none

Correct:

# application.yml
management:
endpoints:
web:
exposure:
include: health, info

Explanation

Exposing Actuator endpoints like health and info can provide essential insights into the application's state and health, which is crucial for maintaining and monitoring the application.

6. Improper Exception Handling

The Mistake

Using default exception handling instead of customizing it can lead to uninformative error responses.

Example

Incorrect:

@RestController
public class MyController {
@GetMapping("/resource")
public ResponseEntity<String> getResource() {
// some code that might throw an exception
}
}

Correct:

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred: " + e.getMessage());
}
}

Explanation

Implementing a global exception handler provides consistent and informative error responses, improving the user experience and debugging process.

Conclusion

Spring Boot is a powerful framework that simplifies Java application development, but it’s essential to avoid common mistakes to harness its full potential. By using starters, externalizing configurations, leveraging constructor injection, utilizing profiles, exposing Actuator endpoints, and implementing proper exception handling, you can create more robust, maintainable, and efficient Spring Boot applications. Keep these tips in mind, and happy coding! 👨‍💻👩‍💻

References

  1. Spring Boot Documentation
  2. Spring Guides
  3. Baeldung — Spring Boot
  4. Mkyong — Spring Boot Tutorials

--

--