In the following blog post I want to take a closer look at the question if the application framework Spring Boot is still relevant in a modern Java based application development. I will take a critical look against its architectural concept and compare it against the Jakarta EE framework. I am aware of how provocative the question is and that it also attracts incomprehension. Comparing both frameworks I am less concerned about the development concept but more with the question about runtime environments.
Both – Spring Boot and Jakarta EE – are strong and well designed concepts for developing modern Microservices. When I am talking about Jakarta EE and Microservices I always talk also about Eclipse Microprofile which is today the de-facto standard extension for Jakarta EE. Developing a Microservice the concepts of Spring Boot and Jakarta EE are both very similar. The reason is, that a lot of technology of today’s Jakarta EE was inspired by Spring and Spring Boot. The concepts of “Convention over Configuration“, CDI or the intensive usage of annotations were first invited by Spring. And this is proof of the innovative power of Spring and Spring Boot. But I believe that Jakarta EE is today the better choice when looking for a Microservice framework. Why do I come to this conclusion?
API vs Implementation
If you implement a Microservice on Jakarta EE you are implementing against an API instead of a concrete implementation. This aspect becomes obvious when you take a look at the pom.xml of a Maven project.
....
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakarta.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>${microprofile.version}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
.....
</dependencies>
.....
The crucial dependencies are marked as ‘provided
‘. This means you expect that the implementation is part of your runtime and not bundled with your artefact. As a result your artefact is much more smaller than a Spring Boot artefact. To run your application you need to deploy it into a Jakarta EE runtime environment.
Spring Boot on the other side builds a bootable artefact containing all necessary libraries and provides you with a bootable server. No additional runtime or application server is necessary. This makes Spring Boot so attractive for developers and was the most important concept for the success of Spring Boot. But this concept was introduced more than 7 years ago. It was a time when installing an application server was a pain – especially for developers. With Spring Boot developers were able to setup a running version of a simple Microservice in just a few simple steps.
Container Environments…
Today we have established the concept of container environments. And every developer can start any kind of server or runtime environment with one simple Docker command. Starting a modern application server like Payara, Wildfly or OpenLiberty within a container environment takes only a few seconds and deploying a Microservice only a few seconds more. For example, to bundle your microservice with the latest Wildfly Docker image, a Dockerfile looks like this:
FROM jboss/wildfly
ADD my-app.war /opt/jboss/wildfly/standalone/deployments/
and to start your service you simply run:
$ docker build --tag=my-app . && docker run -it my-app
And this levels the initial advantage of Spring Boot. Beside the fact that a Jakarta EE artefact is much more smaller, you can deploy your microservice into different environments within seconds. And this gives you much more flexibility about your runtime environment. But this is not the only advantage.
Get Rid of the Dependencies….
The much more interesting aspect of using Jakarta EE is the fact that you do not have hard coded dependencies on certain libraries. Looking again at the pom.xml of a Spring Boot project, you will find a lot of dependencies bundled with your project.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.4.0</version>
</dependency>
....
All the libraries like a JPA Implementation or a Jax-RS implementation will become part of your final build. In contrast in a Jakarta EE project you do not know which JPA or Jax-RS implementation will be used in the target runtime. This means your application is also more interoperable. This becomes all the more evident when developers start to use certain features of a concrete implementation. It often happens that Spring Boot developers use a specific function of an API like the Rest API Jersey to solve a problem. But in this moment your application is no longer compatible with other Jax-RS implementations like for example Rest Easy. And that’s a big threat to fall for. It is a good practice to develop against an API instead of a concrete implementation.
Log4j….
How dangerous the bundling of libraries can be, has recently become clear with the Log4j bug. A simple and typical Spring Boot dependency like this…
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
…can lead to a security vulnerability in your final application. Since the Log4j library is now part of your build. To update this dependency you need to update your code and rebuild and roll out your Spring Boot application.
In contrast, in Jakarta EE you never build your code against a specific implementation, but only against interfaces. This means that your code and thus your final artefact never has a hard-coded dependency on a particular implementation. In the concrete example you just have to change the runtime environment without the need to update or rebuild your code. This means for a container environment like Kubernetes you just have to update the image version and restart the container.
Conclusion
I would like to make it clear once again that I am not talking about the APIs of the two application frameworks. Both, Spring Boot and Jakarta EE, offer a comparable range of functionality to develop modern Microservices in a fast an easy way.
However, the original advantage of Spring Boot to build a bootable server seems more and more a disadvantage in today’s time of container environments. You lose flexibility and run the risk of becoming very dependent on libraries whose effects you can not oversee as a developer. In contrast, modern application servers today offer the use of container technology and enable you to work on a production-like server system during development. From this point of view, in my opinion, it no longer makes sense to build a bootable server around your microservice today.
Thank you, this is a great review and I think you are right.
Many developers like Spring Boot a lot, however, because they have years of experience and have kicked JavaEE back in the times where it was considered bloatware, complex, and hard to use.
Over time, JavaEE/JakartaEE has improved a lot in terms of usability, and it still maintains the advantage it always had: Develop against an API and run against a number of different implementations. Today, we see kind of an inversion in the debate: Spring Boot is considered overly complex and prone to “dependency hell” issues, while JakartaEE is considered lean and clean, also because there are different, defined profiles to suit different requirements.
Considering that, I am quite sure that we will see a revival of JakartaEE relative to Spring Boot in the near future.
My friend, this is exactly the comparation I’ve been looking for. I’m a Spring developer learning Jakarta EE and I think it’s just beautiful and clean how the applications relies only on specifications’ APIs. It made me question if an application built with only Jakarta EE isn’t better for cloud, as it will only deploy business logic and no infrastructure dependencies bundled in the artifact. From your text, seems like this is the case, right?
Do you have any sources to suggest with discussions on this point?
@Douglas, yes your application is quite small because the Runtime Container (e.g. Wildfly, Payara, OpenLiberty) brings the necessary implementations.