MicroProfile

By now you have probably heard of Eclipse MicroProfile (MP). It is a community-driven initiative to define specifications for enterprise Java microservices. MicroProfile is only two years old, yet it has delivered eight innovative specifications and is evolving fast. It provides metrics, API documentation, health checks, fault tolerance, distributed tracing, and more. With it, you can take full advantage of cutting-edge cloud-native technologies and do it in a vendor-neutral fashion!

For developers familiar with Spring Boot, we have prepared this article, which compares the basics of developing applications with Spring Boot and with MicroProfile. We wrote two applications, one with each solution. In this article, we will go through the differences between them. You can find the source code for both projects on GitHub.

For the MicroProfile application, we use Thorntail (formerly know as Wildfly Swarm), but except for the setting up part, Open Liberty, Payara, TomEE, or any other implementation would look exactly the same.

Throughout this article, we assume you know Spring Boot and we focus on what is different in MicroProfile.

Setting up the project

We set up both applications using Maven.

With Thorntail, the setup of a project is quite similar to Spring Boot. The first difference is in the project packaging. While we use jar (the default packaging) for a Spring Boot app, we need to set it to war to package a Thorntail application.

Our Spring Boot application uses a BOM file called spring-boot-dependencies. Thorntail provides BOM files too. We selected bom, which lists all stable, well-tested Thorntail elements. If you like to experiment, you can use bom-all instead, for example.

Both Spring Boot and Thorntail use a Maven plugin to package user's classes, resources, and all the bits of the selected solution into a fat JAR. For Thorntail, the plugin is called thorntail-maven-plugin. The listing below shows its declaration together with the declaration of the Thorntail BOM.

<project ...>
 ...
 <packaging>war</packaging>

<dependencyManagement>
 <dependencies>
   <dependency>
     <groupId>io.thorntail</groupId>
     <artifactId>bom</artifactId>
     <version>${version.thorntail}</version>
     <type>pom</type>
     <scope>import</scope>
   </dependency>
 </dependencies>
</dependencyManagement>
...
<build>
 <plugins>
   <plugin>
     <groupId>io.thorntail</groupId>
     <artifactId>thorntail-maven-plugin</artifactId>
     <version>${version.thorntail}</version>
     <executions>
       <execution>
         <goals>
           <goal>package</goal>
         </goals>
       </execution>
     </executions>
   </plugin>
 </plugins>
</build>

Having defined the basics, we can select the features we need.

While for Spring Boot we chose spring-boot-starter-web, we simply add a dependency on microprofilefor Thorntail.

<dependency>
 <groupId>io.thorntail</groupId>
 <artifactId>microprofile</artifactId>
</dependency>

You can take a look at the final pom.xml file in the GitHub project.

There is one more difference in setting up the project: the place where the static resources should be put. Our Spring Boot application keeps static resources in the src/main/resources/static directory, while the Thorntail application requires them to be placed in src/main/webapp.

Exposing a REST endpoint

The most idiomatic way to expose a REST API from a Spring application is by using Spring MVC. MicroProfile leverages JAX-RS for that purpose.
The translation from one to another is quite straightforward. Both work on annotations.

First, for JAX-RS, an Application class is required. It extends javax.ws.rs.core.Application and can provide a global path prefix for all JAX-RS endpoints. In our case it is:

@ApplicationPath("/api")
public class ApplicationConfig extends Application {
}

Then it’s just a matter of translating the resource classes annotations.

The following Spring MVC code:

@RestController
@RequestMapping(value = "/api/greeting", produces = MediaType.APPLICATION_JSON_VALUE)
public class GreetingController {

   @GetMapping
   public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
      ...
   }

translates to the following MicroProfile code:

@Path("/greeting") // 1
@Produces(MediaType.APPLICATION_JSON) // 2
public class GreetingResource {

   @GET // 3
   public Response greeting(@QueryParam("name") @DefaultValue("World") String name) {  // 4
      ...
   }

To break it down:

  1. The JAX-RS endpoint class should be annotated with @Path. This annotation is also the place to provide any prefix for the endpoint.
  2. A separate annotation, @Produces, has to be used if we want to specify the content type of the responses. It has a counterpart named @Consumes to specify accepted request entity types.
  3. In addition, the methods that are meant to handle the requests need to be annotated with @GET, @POST, @DELETE, @PUT, or @HEAD, depending on the HTTP method they are meant to handle.
  4. Our endpoint uses a query parameter. To make it work, we used a @RequestParam annotation in Spring MVC. For MicroProfile, the annotation to use is @QueryParam. To provide a default value, we added a @DefaultValue annotation.

See the javax.ws.rs package for other available annotations.

It is important to also mention the @Context annotation. With it, you can inject values such as HTTP headers or security context to methods or endpoints.

Dependency injection

The core of Spring is its dependency injection. With MicroProfile, you can use CDI instead.

That mainly means using @Inject annotation instead of Spring’s @Autowired annotation:

   @Inject
   private GreetingGenerator generator;

Similarly to Spring, CDI has a concept of beans and these beans live in a certain scope. The @Scope annotation that is used to control the scope for Spring beans translates to one of the @ApplicationScoped, @RequestScoped, etc. CDI annotations.

Here’s an example from the GreetingGenerator class:

@ApplicationScoped
public class GreetingGenerator {
 …
}

Check out the javax.enterprise.context package for the list of all available options.

The translation is quite straightforward. The only catch is the fact that the default scope is different. While in Spring, the default scope is singleton, in CDI it is @Dependent—a scope corresponding to Spring’s prototype.

Configuration

In our Spring application, we inject configuration values with the @Value annotation.

With MicroProfile Config, we achieve the same with the following:

@Inject
@ConfigProperty(name = "greeting.message")
private String message;

The value for the message field above will be taken from META-INF/microprofile-config.properties. It can be overwritten by an environment variable or a system property. MicroProfile Config also provides an easy mechanism to define a custom ConfigSource, an additional source of configuration values.

Execution

Let’s try it all out!

To get the code, clone the GitHub repository:

git clone https://github.com/michalszynkiewicz/from-spring-to-microprofile/

The repository contains two projects in two separate directories. To build any of them, navigate to the appropriate directory and run the following:

mvn clean package

Both Spring Boot and Thorntail generate an uber-JAR. To run the Thorntail one, go into the target directory and execute this:

java -jar microprofile-from-spring-1.0-SNAPSHOT-thorntail.jar

Now, when you go to http://localhost:8080 in your browser, you should be presented with a web page exposed by the selected application. You can try out the application through the browser or access the REST API directly at http://localhost:8080/api/greeting?name=put-your-name-here.

More than a counterpart

While the code for both example applications is quite similar, the MicroProfile one has a couple more pieces of functionality.

As configured, the MicroProfile application additionally exposes:

  • OpenAPI documentation for the REST endpoint, on http://localhost:8080/openapi
  • Application metrics, including thread count, heap usage, etc. on http://localhost:8080/metrics
  • Health checks, ready to use with Kubernetes, on http://localhost:8080/healthcheck

Moreover, without modifying the configuration, it can make use of other MicroProfile specifications, such as Fault Tolerance or a type-safe REST client.

Further reading

If you would like to learn more, you can download a free ebook: Building Microservices with Enterprise Java: A Practical Guide to Eclipse MicroProfile.

Thorntail and Eclipse Microprofile resources:

 

 

Last updated: January 29, 2019