In this article, we'll explore the top 10 mistakes developers make while using Spring Framework and provide practical solutions with code examples to avoid them.
Let’s dive in! 🚀
1️⃣ Not Using Dependency Injection Properly
❌ Bad Code (Manual Object Creation)
public class UserService {
private UserRepository userRepository = new SimpleUserRepository(); // ❌ Manual object creation
}
🔴 Issue: This creates a tight coupling between classes, making unit testing difficult and violating the dependency inversion principle.
✅ Good Code (Using Spring Dependency Injection)
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
✅ Why is this better?
✔ Promotes loose coupling, making it easier to test and maintain
✔ Supports dependency injection, which enhances flexibility
2️⃣ Using Field Injection Instead of Constructor Injection
❌ Bad Code (Field Injection - Harder to Test)
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
}
🔴 Issue: Field injection does not allow immutable dependencies and makes unit testing difficult because mock dependencies cannot be injected easily.
✅ Good Code (Constructor Injection - Recommended)
@Service
public class OrderService {
private final OrderRepository orderRepository;
@Autowired
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
}
✅ Why is this better?
✔ Ensures dependency availability and immutability
✔ Makes testing easier since dependencies can be injected through the constructor
3️⃣ Ignoring @Transactional
in Database Operations
❌ Bad Code (No Transaction Management - Risk of Partial Updates)
public void transferMoney(Long fromAccount, Long toAccount, Double amount) {
accountRepository.debit(fromAccount, amount);
accountRepository.credit(toAccount, amount);
}
🔴 Issue: If an exception occurs after debiting, the transaction isn't rolled back, leading to data inconsistency.
✅ Good Code (Using @Transactional
)
@Service
public class BankService {
@Transactional
public void transferMoney(Long fromAccount, Long toAccount, Double amount) {
accountRepository.debit(fromAccount, amount);
accountRepository.credit(toAccount, amount);
}
}
✅ Why is this better?
✔ Ensures data consistency by automatically rolling back on exceptions
✔ Saves developers from writing manual rollback logic
4️⃣ Using @RestController
Without Proper Exception Handling
❌ Bad Code (No Exception Handling - Returns 500 Internal Server Error)
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.findById(id); // ❌ Throws 500 error if user not found
}
}
🔴 Issue: Without exception handling, the API returns generic errors instead of meaningful responses.
✅ Good Code (Using @ControllerAdvice
for Global Exception Handling)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleNotFound(ResourceNotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
✅ Why is this better?
✔ Provides consistent error responses for better API usability
✔ Enhances API maintainability and debugging
5️⃣ Not Configuring Spring Security Properly
❌ Bad Code (No Authentication & Authorization)
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAllUsers(); // ❌ Open to anyone
}
🔴 Issue: Without authentication, anyone can access sensitive data, leading to security vulnerabilities.
✅ Good Code (Use Spring Security & OAuth2 + JWT)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
}
✅ Why is this better?
✔ Ensures API endpoints are secured with OAuth2 authentication
✔ Prevents unauthorized access to sensitive resources
6️⃣ Not Closing Database Connections Properly
❌ Bad Code (Not Closing Resources Manually)
public List<User> getUsers() throws SQLException {
Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
// ❌ Connection leak if not closed properly
}
🔴 Issue: This leaks database connections, which can exhaust database resources over time.
✅ Good Code (Using Spring Data JPA - Connection Managed Automatically)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}
✅ Why is this better?
✔ Automatically manages database connections, preventing leaks
✔ Reduces boilerplate code
7️⃣ Not Optimizing Hibernate Queries (N+1 Problem)
❌ Bad Code (Fetching Data Inefficiently - Causes N+1 Queries)
@OneToMany(mappedBy = "user")
private List<Order> orders;
🔴 Issue: Fetching users also fetches orders separately, leading to multiple queries.
✅ Good Code (Using @EntityGraph
to Optimize Queries)
@EntityGraph(attributePaths = {"orders"})
@Query("SELECT u FROM User u WHERE u.id = :id")
User findUserWithOrders(@Param("id") Long id);
✅ Why is this better?
✔ Reduces query overhead and improves performance
8️⃣ Using RestTemplate
Instead of RestClient
(Deprecated API)
❌ Bad Code (Using RestTemplate
)
RestTemplate restTemplate = new RestTemplate();
User user = restTemplate.getForObject("https://api.example.com/user/1", User.class);
🔴 Issue: RestTemplate
is deprecated in the newer version of Spring Framework.
✅ Good Code (Using RestClient
)
RestClient restClient = RestClient.builder().baseUrl("https://api.example.com").build();
User user = restClient.get().uri("/user/1").retrieve().body(User.class);
✅ Why is this better?
✔ More modern & efficient API
9️⃣ Not Using Caching for Expensive Queries
❌ Bad Code (Repeated Expensive Database Calls)
public List<Product> getAllProducts() {
return productRepository.findAll();
}
🔴 Issue: Increases database load and slows down response times.
✅ Good Code (Using Spring Cache with Redis)
@Cacheable("products")
public List<Product> getAllProducts() {
return productRepository.findAll();
}
✅ Why is this better?
✔ Improves performance
🔟 Hardcoding Configuration Instead of Using application.properties
❌ Bad Code (Hardcoded Values)
public static final String API_URL = "https://api.example.com";
🔴 Issue: Hardcoding makes changes difficult.
✅ Good Code (Using @Value
)
@Value("${api.url}")
private String apiUrl;
✅ Why is this better?
✔ Easier to manage configurations
🎯 Conclusion
Avoiding these common Spring Framework mistakes will help you write cleaner, more efficient, and secure applications.
✅ Key Takeaways:
✔ Use dependency injection properly
✔ Handle transactions & exceptions carefully
✔ Use modern Spring features like RestClient
& SecurityFilterChain
✔ Optimize database queries & caching
By following these best practices, you'll build better, scalable, and maintainable Spring applications! 🚀
Happy Coding with Spring Framework! 🌱
Comments
Post a Comment
Leave Comment