Heads up… this article is old!

For an updated version of this article, see Let’s Compare: JAX-RS vs Spring for REST Endpoints on the Okta developer blog.

REST endpoints are used just about everywhere you need to decouple your web service and client. Many developers have used Spring or JAX-RS for this purpose. Some have used one but not the other, in this post I’ll go over the the differences between the two using basically the same code. In future posts I’ll show you how easy it is to secure these REST endpoints using Apache Shiro and Stormpath. If you cannot wait until then, you can check out these examples right now.

Update: This follow-up post shows how to secure JAX-RS resources with a single line of code!

JAX-RS stands for Java API for RESTful Web Services, but that is a mouthful, so just about everywhere except for the spec itself, is simply referred to as JAX-RS. This is officially part of Java EE 6, but can be used in a simple servlet container just as easily (as you will see below). It was specifically created to make writing REST resources easier.

The scope of the Spring Framework and its supporting libraries is obviously much greater than just creating a few REST endpoints, but those topics are for a different post. Today we build a few simple CRUD applications to manage Stormtroopers with JSON payloads marshalled/unmarshalled using the Jackson library.

Lay Down the Foundation – Model and DAO

To keep things focused, I’ll leave the Maven dependencies out of this post. You can browse the full source on Github, the pom files should be self explanatory: one for JAX-RS, another for Spring.

First up, we need to get the common bits out of the way. A simple model and DAO will be used in all of the examples to register and manage Stormtrooper objects.

The Stormtrooper object contains an id and a few other attributes: planetOfOrigin, species, and type.

The DAO interface is just as simple, with the basic CRUD methods and an additional ‘list’ method:

The actual implementation of the StormtrooperDao is not important for these examples, If you are interested, you can take a look at code for DefaultStormtrooperDao, which generates 50 random Stormtroopers.


Now that we have the common bits out of the way, we can get into the meat of our Spring example. A basic Spring Boot app doesn’t get much easier than this:

There are a few things to point out:

  • The @SpringBootApplication annotation sets up Spring’s auto configuration and classpath scanning of components
  • @Bean binds an instance of DefaultStormtrooperDao to the StormtrooperDao interface
  • The main method starts the application uses the SpringApplication.run() helper method to bootstrap the application

Spring Controller

Next up, we have the implementation of our REST endpoint, or in the Spring world a Controller. We will use this class to map our DAO to incoming HTTP requests.

Let’s break this down:

The @RestController is a convenience annotation for both @Controller and @ResponseBody which marks this class as a web component discovered during classpath scanning. An @RequestMapping annotation at the the class level defines the base path mapping used for any other RequestMapping annotations in this class. In this case, all end points in this class will start with the URL path /troopers.

The @RequestMapping annotation has many options, this example uses a small subset:

  • path = "/{id}" used in conjunction with @PathVariable("id") maps the {id} part of the URL path to the given method argument – Example URL: /troopers/FN-2187
  • method = RequestMethod.GET, POST, DELETE define the HTTP request method that is accepted
  • value = HttpStatus.NO_CONTENT sets the expected HTTP response code, in this case a 204

Method parameters annotated with @RequestBody will be deserialize from the HTTP request before getting passed into the method. Return values are directly serialized to HTTP response using the @ResponseBody annotation (or simply by using @RestController), which will also bypass any MCV templates.

In this code block the updateTrooper() method accepts HTTP POST requests made to /trooper/{id} and contain a serialized Stormtrooper (JSON). If the request path was /troopers/FN-2187, the id portion of the path would be assigned to the method’s id parameter. An updated Stormtrooper object is returned and serialized to the HTTP response.

Run the Spring Example

To run this example, grab the source, change to the spring-boot directory, start the application using mvn spring-boot:run, make requests to the server.

To get get a list of all the Stormtroopers just make a request to /troopers

To get a single Stormtrooper, use its ID:

Pretty easy, now stop the server with a Ctrl-C and move on to the next example.

Rinse and Repeat – JAX-RS

We’ll use the same model and DAO for the JAX-RS example, all we’re going to change is the annotations on the StormtroooperController class.

Since JAX-RS is an API spec you need to pick an implementation, we will use Jersey for this example. While it’s possible to create a JAX-RS application with no direct dependencies on a specific JAX-RS implementation, it would make for a more verbose example.

I picked Jersey for a couple reasons, mostly though, it was because I already knew how get simple dependency injection working without jumping through any hoops, we are comparing this to Spring after all. Apache Shiro has an example that runs the same code on Jersey, RestEasy, and Apache CXF, if you are interested in seeing a portable example.

This example also differs a bit from the Spring Boot one in that this example is packaged as a WAR, and Spring Boot was a single JAR. Packing this example in an executable jar is possible, but outside the scope of this post.

A JAX-RS equivalent to a SpringBootApplication is an Application class. A Jersey subclass of Application, ResourceConfig, adds a few handy utility methods. The following snippet configures classpath scanning to detect our individual resource classes, and bind a DefaultStormtrooperDao instance to the StromtrooperDao interface.

One other thing to point out, in the above class the @ApplicationPath annotation marks this class as a JAX-RS Application bound to a specific url path, in our case to match the Spring example above we will just use the root path: /. Each resource detected in the resources package will be appended to this base path.

The JAX-RS resource implementation looks very similar to the Spring version above (renamed to StormtroooperResource, to match naming conventions):

To break down this example a bit, we first have the class deceleration:

Similar to the Spring example above the @Path at the class level means each annotated method in this class will be under the /troopers base path. The @Produces annotation defines the default response content type (unless overridden by an annotation on another method).

Unlike the Spring example where an @RequestMapping annotation defined the path, method, and other attributes of the request, in a JAX-RS resource each attribute uses a separate annotation. Similar to above if we break down the updateTrooper() method:

We see that @Path("/{id}") along with @PathParam("id") allows the id portion of the path to be translated into a method argument. What differs from the Spring example, is that the Stromtrooper parameter and return value do not need extra annotations, they are automatically serialized/deserialized into JSON due to the @Produces("application/json") annotation on this class.

Run the JAX-RS Example

This example can be started from the jersey directory, using the maven command: mvn jetty:run.

Making the same two requests as above, we can list all of the troopers with a GET request to the base resource:

Or again to a GET to a specific resource:

Now we have seen basically the same code run in both Spring and JAX-RS applications by simply changing the annotations. I like the JAX-RS annotations better, they’re more concise. That said, why choose between the two? Jersey and RestEasy both support Spring (along with Guice and CDI/Weld). Lets create a third example combining the two.

JAX-RS and Spring – So Happy Together

For this example we need three classes: a Spring Boot application, Jersey configuration, and our resource.

Our SpringBootApp and StormtrooperResource classes are identical to the previous versions, the only difference being the Jersey configuration class:

This class is a bit slimmer than the previous example. First, you probably noticed the @Configuration annotation which is used to mark this class managed by Spring. All that is left, is to instruct Jersey to scan the resources package again, the rest is handled for you (see what I did there?!).

From the spring-jaxrs directory, this example can be started with the same mvn spring-boot:run command.

Spring to JAX-RS Cheat Sheet

This is not an exhaustive list, but it does include the most common annotations.

Spring Annotation JAX-RS Annotation
@RequestMapping(path = "/troopers" @Path("/troopers")
@RequestMapping(method = RequestMethod.POST) @POST
@RequestMapping(method = RequestMethod.GET) @GET
@RequestMapping(method = RequestMethod.DELETE) @DELETE
@ResponseBody N/A
@RequestBody N/A
@PathVariable("id") @PathParam("id")
@RequestParam("xyz") @QueryParam('xyz")
@RequestParam(value="xyz" @FormParam(“xyz”)
@RequestMapping(produces = {"application/json"}) @Produces("application/json")
@RequestMapping(consumes = {"application/json"}) @Consumes("application/json")

Wrapping things up

So when should you use JAX-RS over Spring Rest? In my opinion it breaks down like this:

If you are already a heavy Spring shop, just use Spring. If you are creating an object JSON / XML REST layer, JAX-RS resources backed by the DI framework of your choice (Spring, Guice, etc) might be the way to go. Rendering pages server side is not part of the JAX-RS spec (though it is supported with extensions). I hacked up a Thymeleaf view for Jersey once, but I think Spring MVC takes the cake here.

Now, comparing a Spring Boot application and an WAR packaged application, isn’t exactly comparing apples to apples. Dropwizard (which uses an embedded Jetty container and Jersey) is probably the closest thing to a SpringBoot app. Hopefully this post gave you a bit more background so you can do your own comparison. I’ll be posting more about Apache Shiro’s new JAX-RS module in the upcoming weeks. Until then you can checkout a Apache Shiro + Stormpath + JAX-RS example, hit me up on Twitter, or leave a comment below.

Want to learn more about Stormpath, Apache Shiro, or REST fundamentals? Take a look at these posts: