Spring Boot is a widely used and very popular enterprise-level high-performance framework. Here are some best practices and some tips you can use to improve your Spring Boot application and make it more efficient. This post is going to be a bit long and it will take some time to read through it in its entirety.
correct packaging style
- Proper packaging will help to easily understand the code and flow of the application.
- You can structure your application with meaningful wrappers.
- You can have all controllers in separate packages, services in separate packages, util classes in separate packages...etc. This style is very convenient in small microservices.
- If you're dealing with a huge codebase, you can use a feature-based approach. You can decide according to your requirements.
organized by type
organized by function
Using the Spring Boot starter
- This is a cool feature of Spring Boot.
- We can use start dependencies very easily without adding individual dependencies one by one. These starter dependencies are already bundled with the required dependencies.
- For example, if we add the spring-boot-starter-web dependency , by default it will be bundled with the jackson, spring-core, spring-mvc and spring-boot-starter-tomcat dependencies .
- So we don't need to care about adding dependencies separately.
- It also helps us avoid version mismatches.
use the correct version of dependencies
- It is always recommended to use the latest stable GA release.
- Sometimes it may vary due to Java version, server version, application type, etc.
- Don't use different versions of the same package, always use <properties> to specify versions if there are multiple dependencies.
Lombok
- As a Java developer, you've probably heard of Project Lombok .
- Lombok is a Java library that reduces code and allows you to write clean code using its annotations.
- For example, you might have lots of getter and setter lines in some classes like entities, request/response objects, dtos, etc.
- But if you use Lombok, it's just one line and you can use @Data, @Getter or @Setter according to your requirement .
- You can also use Lombok logger annotations. Recommend @Slf4j.
Using constructor injection with Lombok
- When we talk about dependency injection, there are two types.
- One is " constructor injection " and the other is " setter injection ". Besides that, you can also do " field injection " using the very popular @Autowired annotation .
- But we strongly recommend using constructor injection over other types. Because it allows the application to initialize all required dependencies at initialization time.
- This is very useful for unit testing.
- Importantly, we can use Lombok's @RequiredArgsConstructor annotation to use constructor injection.
Logging with slf4j
- Logging is very important.
- If your application breaks in production , logging is the only way to find the root cause.
- Therefore, careful consideration should be given before adding loggers, log message types, logger levels, and logger messages.
- Do not use System.out.print()
- It is recommended to use Slf4j with logback, the default logging framework in Spring Boot.
- Always use slf4j { } and avoid string interpolation in logger messages. Because string interpolation consumes more memory.
- You can create loggers very easily using the Lombok @Slf4j annotation.
- If you are in a microservices environment, you can use the ELK stack.
Only use controllers for routing
- Controllers are dedicated to routing.
- It is stateless and singleton .
- DispatcherServlet will check for @RequestMapping on the controller
- The controller is the final destination of the request, and the request will be handed over to and handled by the service layer.
- Business logic should not reside in controllers.
Use services to implement business logic
- Complete business logic including validation, caching, etc.
- The service communicates with the persistence layer and receives the results.
- Services are also singletons.
Avoid Null Pointer Exceptions
- To avoid NullPointerException you can use Optional from java.util package .
- You can also use an empty security library. For example: Apache Commons StringUtils
- Call the equals() and equalsIgnoreCase() methods on known objects.
- Use valueOf() instead of toString()
- Use IDE-based @NotNull and @Nullable annotations.
Best Practices for Using the Collections Framework
- Use the appropriate collection for your dataset.
- Use forEach with Java 8 features and avoid legacy for loops.
- Use interface types instead of implementations.
- Use isEmpty() instead of size() for better readability.
- Don't return null, you can return an empty collection.
- If you are using objects as data to be stored in a hash-based collection, you should override the equals() and hashCode() methods.
use pagination
- This will improve the performance of the application.
- If you use Spring Data JPA , the PagingAndSortingRepository makes working with pagination very easy and almost effortless.
use cache
- Caching is another important factor when talking about application performance.
- By default, Spring Boot provides caching via ConcurrentHashMap , you can achieve this via @EnableCaching annotation.
- If you are not satisfied with the default cache, you can use Redis , Hazelcast or any other distributed cache implementation.
- Both Redis and Hazelcast are memcaching approaches. You can also use a database cache implementation.
Using custom exception handlers and global exception handling
- This is very important when working with large enterprise applications.
- In addition to general exceptions, we may also have scenarios to identify some specific error conditions.
- Exception advisors can be created using @ControllerAdvice where we can create individual exceptions with meaningful details.
- It will make it easier to identify and debug errors in the future.
Use a custom response object
- A custom response object can be used to return an object that contains some specific data and satisfies HTTP status codes, API codes, messages, etc.
- We can use the builder design pattern to create custom response objects with custom properties.
Remove unnecessary code, variables, methods and classes.
- Unused variable declarations will take up some memory.
- Remove unused methods, classes, etc. as it affects the performance of the application.
- Try to avoid nested loops. You can use a map instead.
usage notes
- Don't abuse annotations. Don't comment all code. Instead, you write descriptive code using meaningful words like class, function, method, variable, etc.
- Remove comment code, misleading comments and story comments.
- You can use comments to warn and explain something that is hard to understand at first glance.
Use meaningful words for classes, methods, functions, variables, and other attributes
- It seems simple, but the impact is huge.
- Always use correct meaningful and searchable naming conventions with correct capitalization.
- Usually, we use nouns or phrases when declaring classes , variables and constants . For example: string firstName, const isValid
- You can use verbs and phrases with adjectives to refer to functions and methods . For example: readFile(), sendData()
- Avoid abbreviated variable names and names that are intended to be revealing . For example: int i; string getExUsr;
- If you use it meaningfully, you can reduce the declaration comment lines. Since it has a meaningful name, new developers can easily understand it by reading the code.
Use correct capitalization for declarations
- There are many different cases like uppercase, lowercase, camelCase, PascalCase, snakeCase, SCREAMING_SNAKE_CASE, dashCase, etc.
- But we need to identify which case is dedicated to which variable.
- Usually, I follow,
Class — PascalCase
Methods and Variables - CamelCase
Constant — SCREAMING_SNAKE_CASE
Database related fields —snake_case
- This is just an example. It may differ from the standards you follow at your company.
Simpler
- Always try to write simple, readable code.
- The same simple logic can be implemented in different ways, but it's hard to understand if it's not readable or understandable.
- Sometimes complex logic consumes more memory.
- Try to use KISS , DRY and SOLID principles when writing code .
Use a common code formatting style
- Formatting styles vary by developer. A change in coding style is also considered a change and can make code merging very difficult.
- To avoid this, teams can adopt a common coding format.