Share
Comparing Native Java REST API Frameworks - Devoxx France 2022
Use Spring Boot! No, use Micronaut!! Nooooo, Quarkus is the best!!! What about Helidon?
There are a lot of developers praising the hottest, and fastest, Java REST frameworks: Micronaut, Quarkus, Spring Boot, and Helidon. In this session, you'll learn how to do the following with each framework:
✅ Build a REST API
✅ Secure your API with OAuth 2.0
✅ Optimize for production with Docker and GraalVM
I'll also share some performance numbers and pretty graphs to compare community metrics.
YouTube recording: https://youtu.be/dp3YbdIEyWU
Related blog post: https://developer.okta.com/blog/2021/06/18/native-java-framework-comparison
Helidon companion post: https://developer.okta.com/blog/2022/01/06/native-java-helidon
GitHub repo: https://github.com/oktadev/native-java-examples
Matt Raible
Transcript
86 slides
-
Matt Raible | @mraible June 7, 2022 Native Java REST
API Comparison Micronaut, Quarkus, Spring Boot, and Helidon Photo by Trish McGinity https://www.mcginityphoto.com/Portfolio/TravelandLeisure/France/i-V4VqtL5 -
@mraible Who is Matt Raible? Father, Husband, Skier, Mountain Biker,
Whitewater Rafter Bus Lover Web Developer and Java Champion Okta Developer Advocate Blogger on raibledesigns.com and developer.okta.com/blog @mraible -
@mraible Today’s Agenda Why Java? Build { REST, GraphQL }
APIs with Java Secure your APIs with OAuth 2.1 Build with Docker Go Native with GraalVM https://unsplash.com/photos/JsTmUnHdVYQ -
@mraible Why Java? 25+ Years of use, abuse, and improvements
Open Source code is available; many popular open source frameworks and tools Hugely Popular and widely used by many enterprises and web-scale companies -
@mraible Download the Oracle builds of OpenJDK https://jdk.java.net/18 Or Eclipse
builds from Adoptium https://adoptium.net Get Started with Java 18 -
@mraible Get Started with Java 18 Better yet, use SDKMAN!
curl -s https://get.sdkman.io | bash sdk install java 18-open -
@mraible sdk install micronaut mn create-app com.okta.rest.app \ -b maven
-f security-jwt -f micronaut-aot Get Started with Micronaut -
package com.okta.rest.controller; import io.micronaut.http.MediaType; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.Produces;
import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import java.security.Principal; @Controller("/hello") public class HelloController { @Get @Secured(SecurityRule.IS_AUTHENTICATED) @Produces(MediaType.TEXT_PLAIN) public String hello(Principal principal) { return "Hello, " + principal.getName() + "!"; } } -
Test Micronaut with HTTPie https://httpie.org mvn mn:run http :8080/hello TOKEN=eyJraWQiOiJxOE1QMjFNNHZCVmxOSkxGbFFWNlN...
http :8080/hello Authorization:"Bearer $TOKEN" -
@mraible Get Started with Quarkus mvn io.quarkus:quarkus-maven-plugin:2.9.2.Final:create \ -DprojectGroupId=com.okta.rest \
-DprojectArtifactId=quarkus \ -DclassName="com.okta.rest.quarkus.HelloResource" \ -Dpath="/hello" \ -Dextensions="smallrye-jwt,resteasy-reactive" -
package com.okta.rest.quarkus; import io.quarkus.security.Authenticated; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces;
import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.SecurityContext; import java.security.Principal; @Path("/hello") public class HelloResource { @GET @Authenticated @Produces(MediaType.TEXT_PLAIN) public String hello(@Context SecurityContext context) { Principal userPrincipal = context.getUserPrincipal(); return "Hello, " + userPrincipal.getName() + "!"; } } -
Test Quarkus with HTTPie https://httpie.org mvn quarkus:dev http :8080/hello TOKEN=eyJraWQiOiJxOE1QMjFNNHZCVmxOSkxGbFFWNlN...
http :8080/hello Authorization:"Bearer $TOKEN" -
@mraible Get Started with Spring Boot http https://start.spring.io/starter.zip \ dependencies==web,oauth2-resource-server,native
\ packageName==com.okta.rest \ name==spring-boot \ type==maven-project \ baseDir==spring-boot | tar -xzvf - -
package com.okta.rest.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.security.Principal; @RestController public
class HelloController { @GetMapping("/hello") public String hello(Principal principal) { return "Hello, " + principal.getName() + "!"; } } -
@mraible Optimize Tomcat for Spring Native <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions>
<exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> </exclusion> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.tomcat.experimental</groupId> <artifactId>tomcat-embed-programmatic</artifactId> <version>${tomcat.version}</version> </dependency> -
Test Spring Boot with HTTPie https://httpie.org mvn spring-boot:run http :8080/hello
TOKEN=eyJraWQiOiJxOE1QMjFNNHZCVmxOSkxGbFFWNlN... http :8080/hello Authorization:"Bearer $TOKEN" -
@mraible Get Started with Helidon mvn -U archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \ -DarchetypeArtifactId=helidon-quickstart-mp \ -DarchetypeVersion=2.5.0 \ -DgroupId=com.okta.rest \ -DartifactId=helidon \ -Dpackage=com.okta.rest -
import io.helidon.security.Principal; import io.helidon.security.SecurityContext; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces;
import javax.ws.rs.core.Context; import java.util.Optional; import static javax.ws.rs.core.MediaType.TEXT_PLAIN; @Path("/hello") public class HelloResource { @GET @Produces(TEXT_PLAIN) public String hello(@Context SecurityContext context) { Optional<Principal> userPrincipal = context.userPrincipal(); return "Hello, " + userPrincipal.get().getName() + "!"; } } -
import com.okta.rest.controller.HelloResource; import org.eclipse.microprofile.auth.LoginConfig; import javax.enterprise.context.ApplicationScoped; import javax.ws.rs.core.Application; import java.util.Set;
@LoginConfig(authMethod = "MP-JWT") @ApplicationScoped public class HelloApplication extends Application { @Override public Set<Class<? >> getClasses() { return Set.of(HelloResource.class); } } -
Test Helidon with HTTPie https://httpie.org mvn package && java -jar
target/helidon.jar http :8080/hello TOKEN=eyJraWQiOiJxOE1QMjFNNHZCVmxOSkxGbFFWNlN... http :8080/hello Authorization:"Bearer $TOKEN" -
@mraible Startup Performance Milliseconds 0 300 600 900 1200 Micronaut
Quarkus Spring Boot Helidon 919 921 334 262 686 997 227 Dev Startup (mvn) Packaged Startup (java -jar) -
@mraible What about GraphQL APIs? Why GraphQL? Does your favorite
framework support GraphQL? Micronaut https://micronaut-projects.github.io/micronaut-graphql/latest/guide Quarkus https://quarkus.io/guides/smallrye-graphql Spring Boot https://spring.io/projects/spring-graphql Helidon https://helidon.io/docs/v2/#/mp/graphql/01_mp_graphql -
@mraible Secure your API with OAuth 2.1 https://oauth.net/2.1 PKCE is
required for all clients using the authorization code flow Redirect URIs must be compared using exact string matching The Implicit grant is omitted from this specification The Resource Owner Password Credentials grant is omitted from this specification Bearer token usage omits the use of bearer tokens in the query string of URIs Refresh tokens for public clients must either be sender-constrained or one-time use -
@mraible Authenticate with OpenID Connect (OIDC) What is OpenID Connect?
Does your favorite framework support OIDC authentication? Micronaut https://guides.micronaut.io/latest/micronaut-oauth2-okta.html Quarkus https://quarkus.io/guides/security-openid-connect-web-authentication Spring Boot https://docs.spring.io/spring-security/reference/servlet/oauth2/login Helidon https://helidon.io/docs/v2/#/mp/security/02_providers#_oidc_provider -
@mraible Build with Docker Create a Dockerfile FROM openjdk:18-alpine
ARG JAR_FILE=target/*.jar COPY ${JAR_FILE} app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/app.jar"] -
@mraible Build with Docker Build your image docker build -t
<tag-name> . Run your image docker run -it -p8080:8080 <tag-name> -
@mraible Build with Docker: Jib Get Jibby with it! mvn
verify jib:build Or build directly to your Docker daemon mvn verify jib:dockerBuild https://github.com/GoogleContainerTools/jib -
@mraible Build with Docker Micronaut uses Jib, but you must
configure plugins Quarkus generates four Docker-related files Dockerfile.jvm Dockerfile.legacy-jar Dockerfile.native Dockerfile.native-micro Quarkus + Jib mvn quarkus:add-extension -Dextensions="container-image-jib" -
@mraible Build with Docker Spring Boot 2.3+ has built-in support
mvn spring-boot:build-image Uses layered JARs for faster builds dependencies snapshot-dependencies resources application https://spring.io/blog/2020/01/27/creating-docker-images-with-spring-boot-2-3-0-m1 -
@mraible Build with Docker Helidon generates three Docker-related files Dockerfile
Dockerfile.jlink Dockerfile.native Helidon + Jib Not available -
@mraible Use Micronaut CLI mn create-app ... mvn package -Dpackaging=native-image
gradle nativeImage gradle dockerBuildNative Go Native with GraalVM and Micronaut https://docs.micronaut.io/latest/guide/#graal -
@mraible Go Native with GraalVM and Quarkus Create an executable
without GraalVM installed mvn package -Pnative -Dquarkus.native.container-build=true Then, build the image docker build -f src/main/docker/Dockerfile.native -t \ <tag-name> . And run it docker run -it -p8080:8080 <tag-name> https://quarkus.io/guides/building-native-image -
@mraible Use start.spring.io to get plugins and profiles <plugin> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <configuration> <classifier>${repackage.classifier}</classifier> <image> <builder>paketobuildpacks/builder:tiny</builder> <env> <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE> </env> </image> </configuration> </plugin> <plugin> <groupId>org.springframework.experimental</groupId> <artifactId>spring-aot-maven-plugin</artifactId> <version>${spring-native.version}</version> <executions> Go Native with GraalVM and Spring Boot -
@mraible Go Native with GraalVM and Spring Boot Add milestone
repositories to your pom.xml <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </pluginRepository> </pluginRepositories> -
@mraible Go Native with GraalVM and Spring Boot Add Spring
Native dependency <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-native</artifactId> <version>0.11.4</version> </dependency> Build the native application mvn spring-boot:build-image -
@mraible Build the image docker build -f Dockerfile.native -t <tag-name>
. And run it docker run --rm -p8080:8080 <tag-name> Go Native with GraalVM and Helidon -
@mraible Native Startup Performance Milliseconds 0 20 40 60 80
100 June 6, 2022 42.4 61 19.2 29.8 Micronaut Quarkus Spring Boot Helidon -
@mraible Native Memory Used (MB) Megabytes 0 25 50 75
100 June 6, 2022 62 62 36 59 Micronaut Quarkus Spring Boot Helidon -
@mraible Stack Overflow Tags 0 35,000 70,000 105,000 140,000 June
6, 2022 67 122,797 2,719 1,352 Micronaut Quarkus Spring Boot Helidon -
@mraible GitHub Stars 0 18,750 37,500 56,250 75,000 June 6,
2022 2,700 61,500 10,000 5,300 Micronaut Quarkus Spring Boot Helidon -
@mraible Jobs on Indeed (US) 0 10,000 20,000 June 6,
2022 16 13,696 188 187 Micronaut Quarkus Spring Boot Helidon -
@mraible Twitter Followers 0 25,000 50,000 75,000 100,000 June 6,
2022 3,712 88,300 15,000 11,800 Micronaut Quarkus Spring Boot Helidon -
@mraible JHipster Support 🤓 Micronaut Blueprint - github.com/jhipster/generator-jhipster-micronaut - v1.0.2,
18 releases, 17 contributors, 385 commits // TODO: Micronaut 3, Reactive, Microservices, GraalVM native images Quarkus Blueprint - github.com/jhipster/generator-jhipster-quarkus - v2.0.0-beta.1, 6 releases, 16 contributors, 550 commits // TODO: Quarkus 2.9, Dev Services, Reactive, Microservices -
@mraible 🏆 Quarkus provides the best DevEx, startup time, and
memory usage 🚀 Micronaut is the easiest to use and has been since the beginning 🌱 Spring Boot has the strongest community, ecosystem, and growth 🔮 Helidon still has some catching up to do but has made great progress ⚡ Spring 6 could be a game changer My Thoughts -
@mraible Action! New to Java? Try Spring Boot Know Spring?
Trial migration paths Testing is important, invest early and often Design your apps with security in mind Use OpenID Connect and OAuth 2.1 https://unsplash.com/photos/JsTmUnHdVYQ