Spring Boot Best Practices


In this article, I would like to discuss a few Spring boot best practices that we can use in our Spring boot applications.

Following are list of Spring boot best practices that we will discuss:

1. Standard Project Structure for Spring Boot Projects
2. Using Java-based configuration - @Configuration
3. Using Auto-configuration
4. Use Spring Initializr for starting new Spring Boot projects
5. When to Use Constructor-based and setter-based DI in Spring or Spring boot applications
6. Using @Service Annotation Class for Business Layer
7. Spring Bean Naming Conventions
8. Handle Circular dependencies
9. Exception Handling in Spring boot Rest API projects
10. Follow Restful API Design Best Practices in Spring Boot Rest API Applications

Video

This article explained in below YouTube video. Subscribe to our YouTube channel for more such videos.

1. Standard Project Structure for Spring Boot Projects

Here, I will show you recommended ways to create a spring boot project structure in Spring boot applications, which will help beginners to maintain standard coding structure.

Don't use the “default” Package

When a class does not include a package declaration, it is considered to be in the “default package”. The use of the “default package” is generally discouraged and should be avoided. It can cause particular problems for Spring Boot applications that use the @ComponentScan@EntityScan, or @SpringBootApplication annotations since every class from every jar is read.
I recommend that you follow Java’s recommended package naming conventions and use a reversed domain name (for example, com.javaguides.projectname).
To know standard package naming conventions, check out - about Java Packages with Examples.
Here, I recommend two approaches to create a standard project structure in Spring boot applications.

Project Structure - First approach

The following spring boot project structure shows a typical layout recommended by spring boot team:

Project Structure - Second approach

However, the above typical layout approach works well but some of the developers prefer to use the following packaging or project structure: 
Separate package for each layer like a model, controller, dao and service etc.

2. Using Java-based configuration - @Configuration

Spring Boot favors Java-based configuration. Although it is possible to use SpringApplication with XML sources, I generally recommend that your primary source be a single @Configuration class. Usually, the class that defines the main method is a good candidate as the primary @Configuration or @SpringBootApplication annotation.
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
    }
}
It is always a good practice to divide a large Spring-based configuration file into multiple files as per your convenience. I generally keep Database configuration in one file (DatabaseConfig), Spring MVC beans in another file, etc, and then finally import in the main file.

The below example demonstrates the same here.

Importing Additional Configuration Classes

You need not put all your @Configuration into a single class. The @Import annotation can be used to import additional configuration classes.
For example:
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(...);
    }
}
Let's import above additional configuration into AppConfig class:
@Configuration
@AnnotationDrivenConfig
@Import(DataSourceConfig.class) // <-- AppConfig imports DataSourceConfig
public class AppConfig extends ConfigurationSupport {
    @Autowired DataSourceConfig dataSourceConfig;

    @Bean
    public void TransferService transferService() {
        return new TransferServiceImpl(dataSourceConfig.dataSource());
    }
}
Multiple configurations may be imported by supplying an array of classes to the @Import annotation:
@Configuration
@Import({ DataSourceConfig.class, TransactionConfig.class })
public class AppConfig extends ConfigurationSupport {
    // @Bean methods here can reference @Bean methods in DataSourceConfig or TransactionConfig
}

3. Using Auto-configuration

Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies that you have added.
The simplest way to make use of it is to rely on the Spring Boot Starters. So, if you want to interact with Redis, you can start by including:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
if you want to work with MongoDB, you have:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
and so on… By relying on these starters, you are relying on a tested and proven configuration that is going to work well together.
It is possible to exclude some classes from the Auto-configuration, by using the following annotation property: @EnableAutoConfiguration(exclude={ClassNotToAutoconfigure.class}), but you should do it only if absolutely necessary.

4. Use Spring Initializr for starting new Spring Boot projects

There are many ways to create a Spring Boot application. The simplest way is to use Spring Initializr at http://start.spring.io/, which is an online Spring Boot application generator.

5. When to Use Constructor-based and setter-based DI in Spring or Spring boot applications

As we know that there are three possible ways of DI in Spring:
  1. Constructor-based dependency injection
  2. Setter-based dependency injection
  3. Field-based dependency injection
From spring Framework documentation, since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.

As I can see many developers prefer to use constructor injection over setter based injection because this makes bean class object as immutable(We cannot change the value by Constructor injection).

For example, define dependencies as final variables like:
@Service
@Transactional
public class ProductService {
    private final ProductRepository productRepository;

    public ProductService(ProductRepository productRepository) {
        this.productRepository= productRepository;
    }
}
This way it will be easier to instantiate ProductService with necessary dependencies for testing if required.

6. Using @Service Annotation Class for Business Layer

I observed in many of the spring boot open-source applications, the Spring Data JPA repositories directly called in Controller classes in Spring boot applications. I generally use Service class annotated with a @Service annotation to write business-related logic so you can follow this approach.
For example, UserService, CustomerService, EmployeeService etc.

I listed a few free spring boot projects here at 10+ Free Open Source Projects Using Spring Boot article.

7. Spring Bean Naming Conventions

The convention is to use the standard Java convention for instance field names when naming beans. That is, bean names start with a lowercase letter, and are camel-cased from then on.
Examples of such names would be (without quotes) 'accountManager', 'accountService', 'userDao', 'loginController', and so forth.
Naming beans consistently make your configuration easier to read and understand, and if you are using Spring AOP it helps a lot when applying advice to a set of beans related by name.

Spring Bean Naming Conventions in Java-based config

Spring Bean Naming Conventions in an annotation-based config

8. Handle Circular dependencies

If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example - Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime and throws a BeanCurrentlyInCreationException.
public  class A {
    public A(B b){
       ....
    }
}
public  class B {
    public B(A b){
       ....
    }
}
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.

9. Exception Handling in Spring boot Rest API projects

Handle proper exceptions and custom error messages for RESTful Web Services developed using Spring Boot.

10. Follow Restful API Design Best Practices in Spring Boot Rest API Applications

I have written a separate article of Restful API design best practicesHere is a list of best practices to design a clean RESTful API.

Comments

Post a Comment