Spring Boot best practices and Spring pitfalls

Updated: 2024-07-12

Set spring.jpa.open-in-view=false

By default, Spring Boot registers an OpenEntityManagerInViewInterceptor (spring.jpa.open-in-view=true).

With this option Spring Boot binds a JPA EntityManager to the thread for the entire processing of the request.

This practice was useful in the past (using JSP and other view renderers) to avoid the LazyInitializationException
when some entities were retrieved in a transaction and, in subsequent code, the application tried to access some lazy
loaded entities linked to the result.

At this point the transaction was already closed and the entities detached.

For most applications this feature is not necessary anymore, some developers wanted to disable it by default
already in Spring Boot 2.

This generated a debate between 'java gurus' on the utility and the performance impact of this binding.

I recommend to start a project setting spring.jpa.open-in-view=false and activate it if necessary. For REST
applications with a transactions managed at service level this interceptor should not be necessary.

De-activating open-in-view will avoid to bind an entity manager to the thread at every request and unbind it at the
end of the request. The code concerned is in the
org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptorclass.

Virtual Threads

If you are using Java 21 or later and Spring 3.2 you can activate Virtual Threads in Spring using:
spring.threads.virtual.enabled=true

If your application has a lot of web requests or is using @Async methods you could see an improvement in the
performances.

Traditionally, each web request open a new thread at OS level, this operation is expensive. With virtual threads the
Java Runtime will create a light thread with an improved utilisation of the resources.

Server compression

This feature can be useful if your deployment doesn't use a proxy (e.g. nginx), we can ask Spring to compress the static
assets and reduce the size of the files sent to the client.

# Enable response compression 
server.compression.enabled=true 
# Mime types that should be compressed 
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json 
# Minimum response where compression will be done 
server.compression.min-response-size=8192 
spring.web.resources.cache.cachecontrol.max-age=15768000 

The best solution is still to use a dedicated proxy server with caching features.

You can find an example in my post: Docker with Angular and Nginx

Cache

Check if your view template engine supports caching in Spring and use it in case of benefits.
Example, this blog mustache and the caching has to be activated: spring.mustache.servlet.cache=true

@Autowired Injection

Avoid field and setter injection

It should be used only for optional dependencies or in test code.

Good

  • easy to use

Bad

  • the bean can be changed runtime with a new call of the setter;
  • the field can reference a null instance, you should add the annotation @Required to enforce the dependency;
  • it's easy to add dependencies, the class risk to violate the single responsibility principle becoming a container of
    services.

References

Use constructor injection

Good

  • the beans cannot be null;
  • the object is immutable;
  • the object can be defined final;
  • in case the bean has only one constructor you can
    omit @Autowired;
  • force to better think the responsibility of the class.

References

If you are using lombok you can use @AllArgsConstructor and simply declare your components as fields.

Lombok will create the constructor that will be used by Spring to inject the components.

lombok constructor

Avoid the use of @Value in Spring Boot

Spring Boot introduced the @ConfigurationProperties annotation that is 'far more superior than the basic @Value
approach' according to Stéphane Nicoll (Pivotal).

The advantages:

  • You inject only an object a POJO and not a list of fields
  • There is less risk to do typos in the declaration of the property
  • The POJO is TypeSafe and can contain complex structures (e.g. 'database.configuration.mysql.connection')

Here you can find the documentation:

Don't create an interface for a single implementation, ideally use interfaces only for multiple implementations

Years ago we had the trend that in a well designed Java application every service / component required a level of
abstraction, a.k.a. an interface.

E.g. a repository interface would have a JDBC, a JPA implementation a mock (test) implementation etc.

In recent years the pragmatism won, and now it's recommended to create an interface only if multiple implementations are
developed.

You can quickly create an interface if required.

Understand how proxies and spring annotations work

Spring proxies your classes and the annotations work on the proxied class. For this reason, e.g. passing through 2 or
more @Transactional methods without an external call between the 2, won't separate the Transaction.

Document your REST APIs and the errors

Your APIs could be used by other teams, add some documentation to them. You can use Swagger
or Spring Rest Doc.
In version 6.0 spring added the
ProblemDetail class
for an
extended error response.

Stay up-to-date with Spring releases

You can find the dates of the releases here: https://calendar.spring.io, it's useful to check when some new releases are
planned.

Java good practices

Use records instead of beans for immutable data

records are a great feature that allows you to easily create immutable data objects. Prefer them to classic beans or
lombok data objects if you need to return data from repositories or you need to transfer data using REST APIs.


WebApp built by Marco using SpringBoot 3.2.4 and Java 21, in a Server in Switzerland