Elevate Your Skills: 6 Must-Know Tips for Spring Boot Developers
Rajat Singh
Lead Developer @Arrow Electronics. Developing and Enhancing SAP E-Commerce(Hybris) Applications. Passionate Coder/Thinker. Lets Connect!
1. Hot Reload on Save to Reduce Development Downtime
Add Dependency
In pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
Enable Auto-Compilation
IntelliJ IDEA: Go to File > Settings > Build, Execution, Deployment > Compiler and enable "Build project automatically".Press Ctrl + Shift + A, search Registry, and enable compiler.automake.allow.when.app.running.
Eclipse: Enable "Build Automatically" from the Project menu.
Restart Settings
settings to Exclude static files and to monitor custom directories:
spring.devtools.restart.exclude=static/**,templates/**
spring.devtools.restart.additional-paths=src/main/custom-dir
2. Optimize Data Fetching with Spring Data Projections
Spring Data Projections lets us fetch only the specific fields you need from the database, skipping the extra baggage of loading the whole entity.
This speeds things up, especially when working with large entities, by cutting down on data transfer and saving memory.
Use Interface-Based Projections
Define an interface with methods matching the required fields
public interface UserProjection {
String getFirstName();
String getLastName();
}
Modify Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<UserProjection> findByActive(boolean active);
}
Manually Map the Projection in your DTO in the Service Layer
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<UserDTO> getActiveUsers() {
List<UserProjection> projections = userRepository.findByActive(true);
// Map projections to DTO
return projections.stream()
.map(p -> new UserDTO(p.getFirstName(), p.getLastName()))
.toList();
}
}
3. Eliminate Boilerplate with Lombok
Lombok is a Java library that reduces boilerplate code by generating common methods like getters, setters, constructors, toString, etc., at compile time using annotations.
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
2. Annotations
@Getter: Generates getter methods for all fields in the class.
@Setter: Generates setter methods for all fields in the class. @ToString: Generates the toString() method for the class, including all fields. @EqualsAndHashCode: Generates equals() and hashCode() methods based on fields. @NoArgsConstructor: Generates a no-argument constructor. @AllArgsConstructor: Generates a constructor with one parameter for each field. @RequiredArgsConstructor: Generates a constructor for final and @NonNull fields. @Builder: Implements the builder pattern, allowing a fluent interface for object creation. @Slf4j: Adds a Logger field for logging using SLF4J. @Value: Makes the class immutable with @Getter, @AllArgsConstructor, and other features. @NonNull: Marks a field or parameter as non-null and adds a null-check. @Cleanup: Automatically closes resources (like streams or connections) at the end of the scope. @Synchronized: Adds synchronization to a method, replacing synchronized keyword. @SuperBuilder: Extends @Builder to allow inheritance-based building of objects. @Accessors: Customizes the getter/setter method names (e.g., enabling fluent setters). @Getter(AccessLevel.PRIVATE): Restricts access level for getters to private. @Delegate: Delegate method calls to a field in the class, avoiding manual delegation.
Example: Below @Builder Annotations on User removes lots of boilerplate code for this User.
领英推荐
// with the LOMBOK
@Builder
public class User {
private String firstName;
private String lastName;
}
// without the LOMBOK
public class User {
private String firstName;
private String lastName;
private User(Builder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public static class Builder {
private String firstName;
private String lastName;
public Builder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public Builder withLastName(String lastName) {
this.lastName = lastName;
return this;
}
public User build() {
return new User(this);
}
}
@Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
4. Use Custom Spring-Boot Starter
Custom starters are a powerful way to encapsulate and standardize common functionality across multiple applications.
you can refer below video to create a simple custom Spring-Boot Starter and to include it in your projects.
5. Migration of Spring-Boot Projects
Update the parent version in pom.xml:
Step 1
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version> <!-- Update to desired version -->
</parent>
Upgrade one minor version at a time (2.5 → 2.6 → 2.7 → 3.0) if there is good gap to the target version you are going to use.
Step 2
Use Spring Boot Dependency Management:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Using dependencyManagement offers several benefits. It automatically resolves dependencies, eliminating the need to specify versions explicitly. Additionally, it helps manage versions of related dependencies consistently.
example :
<!-- With dependency management -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<!-- No version needed - managed by Spring Boot -->
</dependency>
<!-- Without dependency management -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>3.2.3</version> <!-- Have to manually specify version -->
</dependency>
Step 3
use Spring Boot Upgrade Assistant with OpenRewrite.
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>5.2.2</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>5.2.2</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
you can do the dry run and manually review the changes in the changes in target/rewrite/rewrite.patch file.
./mvnw rewrite:dryRun
and then can run the below command to apply the changes
./mvnw rewrite:run
6. IntelliJ IDEA Plugins
Below are the IntelliJ plugins that will simplify your job step further while working on the spring boot projects.
Lombok Plugin: Adds Lombok support to IntelliJ IDEA.
JPA Buddy Plugin: Helps developers work efficiently with Hibernate, EclipseLink, Spring Data JPA, Flyway, Liquibase, Lombok, MapStruct, and other related technologies in both Java and Kotlin.
Spring Boot Helper Plugin: Assist in Spring application development - Adds support for start initializr, autocomplete Spring Boot/Cloud configuration key/value, Spring reference configuration, and Spring metadata documentation. (Support: Java, Kotlin, application.yml)
Thank you for reading.