7316d26c9f0f38c63811e7663dc38aa655e98359
Spring/Common Mistakes in Spring Boot Development.md
| ... | ... | @@ -0,0 +1,167 @@ |
| 1 | +https://blog.devops.dev/common-mistakes-in-spring-boot-development-5548021330cf |
|
| 2 | + |
|
| 3 | +# Common Mistakes in Spring Boot Development | by Java Codeex | DevOps.dev |
|
| 4 | +**Check out my detailed video and join our community by subscribing to my channel. Your support means the world to me!** |
|
| 5 | + |
|
| 6 | +[Lien vers la video Youtube de ce tuto.](https://youtu.be/tc5kV09vD38) |
|
| 7 | + |
|
| 8 | +``` |
|
| 9 | +@Component |
|
| 10 | +public class DateUtils { |
|
| 11 | + public static LocalDate parse(String date) { |
|
| 12 | + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // Adjust the pattern as needed |
|
| 13 | + try { |
|
| 14 | + return LocalDate.parse(date, formatter); |
|
| 15 | + } catch (DateTimeParseException e) { |
|
| 16 | + e.printStackTrace(); |
|
| 17 | + return null; // Or handle the exception as needed |
|
| 18 | + } |
|
| 19 | + } |
|
| 20 | +} |
|
| 21 | +``` |
|
| 22 | + |
|
| 23 | + |
|
| 24 | +Utility classes generally contain static methods and do not need Spring’s dependency injection or lifecycle management. |
|
| 25 | + |
|
| 26 | +Using @Component on a utility class is unnecessary because it does not need to be instantiated or managed by Spring. |
|
| 27 | + |
|
| 28 | +**Impact of Overusing @Component:** |
|
| 29 | + |
|
| 30 | +**Unnecessary Complexity:** Introducing @Component without any actual need adds unnecessary complexity to the application. |
|
| 31 | +**Resource Management:** Spring will create and manage a bean for the utility class, which is an unnecessary use of resources. |
|
| 32 | +**Misleading Semantics:** It might mislead other developers into thinking that the class has dependencies or state that needs to be managed by Spring. |
|
| 33 | + |
|
| 34 | +For utility classes like DateUtils that only contain static methods and do not require any Spring features, it’s best to avoid using @Component. Simply defining the class without any Spring annotations is the most appropriate approach. |
|
| 35 | + |
|
| 36 | +**2\. Incorrect Usage of \`@ResponseBody\` Annotation** |
|
| 37 | +\-Mistake: Overusing \`@ResponseBody\` for all controllers. |
|
| 38 | +\-Tip: Prefer \`@RestController\` for RESTful services to avoid redundancy. |
|
| 39 | + |
|
| 40 | +``` |
|
| 41 | +@RestController |
|
| 42 | +@RequestMapping("/api/employees") |
|
| 43 | +public class EmployeeController { |
|
| 44 | + @Autowired |
|
| 45 | + private EmployeeService employeeService; |
|
| 46 | + @GetMapping |
|
| 47 | + @ResponseBody |
|
| 48 | + public List<Employee> getAllEmployees() { |
|
| 49 | + return employeeService.getAllEmployees(); |
|
| 50 | + } |
|
| 51 | +} |
|
| 52 | +``` |
|
| 53 | + |
|
| 54 | + |
|
| 55 | +**@RestController vs. @Controller:** |
|
| 56 | + |
|
| 57 | +@RestController is a specialized version of @Controller that combines @Controller and @ResponseBody. |
|
| 58 | + |
|
| 59 | +**@Controller** is typically used for MVC controllers where methods return views (HTML, JSP, etc.). |
|
| 60 | +**@RestControlle**r is used for RESTful controllers where methods directly return data (JSON, XML, etc.). |
|
| 61 | + |
|
| 62 | +**The Behavior of @RestController:** |
|
| 63 | + |
|
| 64 | +When you annotate a class with @RestController, it implies that all methods inside the class are by default annotated with @ResponseBody. |
|
| 65 | +This means the return value of each method is serialized directly into the HTTP response body, typically as JSON or XML. |
|
| 66 | + |
|
| 67 | +**Impact of @ResponseBody on @RestController Methods:** |
|
| 68 | + |
|
| 69 | +Adding **@ResponseBody** explicitly on methods inside a @RestController class is redundant and does not provide any additional functionality. |
|
| 70 | +It does not cause errors or affect the behavior of the application negatively, but it adds unnecessary clutter to the code. |
|
| 71 | + |
|
| 72 | +**3\. Improper \`@Autowired\` Injection** |
|
| 73 | +\-Mistake: Using field injection with \`@Autowired\`. |
|
| 74 | +\-Tip: Use constructor-based injection for better testability and immutability. |
|
| 75 | + |
|
| 76 | +``` |
|
| 77 | + |
|
| 78 | +@Service |
|
| 79 | +public class EmployeeService { |
|
| 80 | + @Autowired |
|
| 81 | + private EmployeeRepository employeeRepository; |
|
| 82 | + public List<Employee> getAllEmployees() { |
|
| 83 | + return employeeRepository.findAll(); |
|
| 84 | + } |
|
| 85 | +} |
|
| 86 | +``` |
|
| 87 | + |
|
| 88 | + |
|
| 89 | +In your `EmployeeService` class, using constructor injection instead of field injection (`@Autowired` on the field) is generally considered a better practice. Constructor injection promotes better testability, readability, and helps in managing dependencies more effectively. |
|
| 90 | + |
|
| 91 | +Here’s how you can refactor your `EmployeeService` class using constructor injection: |
|
| 92 | + |
|
| 93 | +``` |
|
| 94 | +public class EmployeeService { |
|
| 95 | + private final EmployeeRepository employeeRepository; |
|
| 96 | + // Constructor injection |
|
| 97 | + public EmployeeService(EmployeeRepository employeeRepository) { |
|
| 98 | + this.employeeRepository = employeeRepository; |
|
| 99 | + } |
|
| 100 | + public List<Employee> getAllEmployees() { |
|
| 101 | + return employeeRepository.findAll(); |
|
| 102 | + } |
|
| 103 | +} |
|
| 104 | +``` |
|
| 105 | + |
|
| 106 | + |
|
| 107 | +**Constructor Injection Benefits:** |
|
| 108 | + |
|
| 109 | +* **Testability:** Constructor injection allows you to easily mock dependencies when writing unit tests for `EmployeeService`. |
|
| 110 | +* **Explicit Dependencies:** It makes dependencies explicit, improving readability and reducing the chance of null pointer exceptions. |
|
| 111 | +* **Immutable Dependencies:** Once initialized, dependencies (like `employeeRepository` in this case) cannot be changed, promoting immutability. |
|
| 112 | + |
|
| 113 | +**Avoiding** `**@Autowired**` **on Fields:** |
|
| 114 | + |
|
| 115 | +* Using `@Autowired` on fields (field injection) can lead to tightly coupled code and makes dependencies less clear. |
|
| 116 | +* Constructor injection avoids these issues by explicitly declaring dependencies in the constructor signature. |
|
| 117 | + |
|
| 118 | +**4\. Mismanagement of \`application.properties\`** |
|
| 119 | +\-Mistake: Hardcoding configuration values in \`application.properties\`. |
|
| 120 | +\-Tip: Utilize Spring profiles (\`application-{profile}.properties\`) to manage configurations for different environments (dev, test, prod). |
|
| 121 | + |
|
| 122 | +**5\. Poor Exception Handling** |
|
| 123 | +\-Mistake:Not implementing global exception handling. |
|
| 124 | +\-Tip:Use \`@ControllerAdvice\` to handle exceptions consistently across your application. |
|
| 125 | + |
|
| 126 | +Your `GlobalExceptionHandler` is set up effectively to handle exceptions globally in your Spring Boot project. By centralizing exception handling, you can maintain consistency in error responses and simplify error management across your application. This approach improves maintainability and enhances the user experience by providing informative and consistent error messages. |
|
| 127 | + |
|
| 128 | +``` |
|
| 129 | +@RestController |
|
| 130 | +@RequestMapping("/api/employees") |
|
| 131 | +public class EmployeeController { |
|
| 132 | + @Autowired |
|
| 133 | + private EmployeeService employeeService; |
|
| 134 | + @GetMapping("/{id}") |
|
| 135 | + public ResponseEntity<Employee> getEmployeeById(@PathVariable Long id) { |
|
| 136 | + Employee employee = employeeService.getEmployeeById(id); |
|
| 137 | + if (employee == null) { |
|
| 138 | + throw new EmployeeNotFoundException("Employee not found with id: " + id); |
|
| 139 | + } |
|
| 140 | + return ResponseEntity.ok(employee); |
|
| 141 | + } |
|
| 142 | + // Other controller methods |
|
| 143 | +} |
|
| 144 | +``` |
|
| 145 | + |
|
| 146 | + |
|
| 147 | +``` |
|
| 148 | +@ControllerAdvice |
|
| 149 | +public class GlobalExceptionHandler { |
|
| 150 | + @ExceptionHandler(EmployeeNotFoundException.class) |
|
| 151 | + public ResponseEntity<String> handleEmployeeNotFoundException(EmployeeNotFoundException ex, WebRequest request) { |
|
| 152 | + return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND); |
|
| 153 | + } |
|
| 154 | + @ExceptionHandler(Exception.class) |
|
| 155 | + public ResponseEntity<String> handleGlobalException(Exception ex, WebRequest request) { |
|
| 156 | + return new ResponseEntity<>("An error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); |
|
| 157 | + } |
|
| 158 | +} |
|
| 159 | +``` |
|
| 160 | + |
|
| 161 | + |
|
| 162 | +**6\. Inefficient Logging Practices** |
|
| 163 | +\-Mistake: Not understanding log levels or logging sensitive data. |
|
| 164 | +\-Tip: |
|
| 165 | +— Understand and use appropriate log levels (ERROR, WARN, INFO, DEBUG, TRACE). |
|
| 166 | +— Configure logging levels for different environments using Spring profiles. |
|
| 167 | +— Avoid logging sensitive information such as user IDs and transaction IDs. |
|
| ... | ... | \ No newline at end of file |