Is Spring Boot Still State of the Art?

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.