RESTful Spring MVC and ExtJs (Episode 1, The Spring Stuff)

My latest adventures have taken me to a client replacing legacy Flex apps with ExtJs apps backed by Spring. In this two part post, you’ll see the product of the many bits of documentation, examples and blog posts I had to cobble together to get this set up, in hopes that it will serve as a complete example for someone else.

Background
If I had my druthers, I’d choose something a little more lightweight for the task at hand – at one time, this was supposed to be a Grails back end, and I was looking forward to getting back in to that mix. That option vaporized and we are now taking it a little slower with a Spring back-end. Thankfully, I work with folks that can make a little lemonade of that lemon, and we’re at least working with pretty close-to-the-latest core Spring, Spring MVC, Spring Data – version 3.2.1.

At the end of this post, you’ll have a working RESTful API provided by Spring using Spring MVC, and some of it’s new features. The source code is on Github so you can follow along. I will walk through the app’s configuration as well as the particulars of the controller class that will serve the front end.

Getting Started
We’ll start with the basic project layout and required jars:

Spring MVC Config
Spring 3.2 provides java-based Spring configuration (docs here). We’ll see a few examples of that here. The dispatcher servlet declaration and config remain in XML – all other bean declarations and config will be in java. First, in our web.xml file (/web/WEB-INF/web.xml), we need to configure the Spring MVC Dispatcher Servlet, as well as the Context Loader and it’s config locations. You can see a few important things here. The dispatcher servlet config itself, and it’s servlet mapping are near the bottom. They handle request routing, and will route requests behind “/spring/” to Spring MVC. We also see two context-params – the first (ContextClass) will tell Spring MVC to accept annotated classes as input in place of XML. The second (contextConfigLocation) will tell Spring MVC in which package to find the annotated classes that will make up the Spring config. Here is the web.xml in question:

Next up is configuring the dispatcher servlet itself in dispatcher-servlet.xml. Not much to do here – we’re simply telling Spring MVC that we’ll be using annotations to drive our config. Note that by default, Spring MVC provides JSON request marshalling/unmarshalling using the given config (more on that later).

Moving On…
…to the interesting parts. We’ll start with the java based config. In this example, we’ll rely on component scanning and autowiring, and keep the config to a minimum. We’ll be scanning the ‘com.rodenbostel.springextjs’ package for more Spring Beans…

RESTful Controller
You can see the annotated controller below. There is quite a bit of config directly in the file, but from the start, you should notice that the class is marked with the controller interface annotation. Spring will recognize this during the context scan and make this a Spring bean. Next, we see five methods – one for each of the 4 familiar operations of CRUD, plus one that retrieves a single item for convenience sake, though still providing the same interface from the caller’s perspective. The 5 methods follow the paradigm used by scaffolded RESTful controllers created by Grails and Rails. In our app, the items the interface is being provided for are called ‘Gizmos’.

For all of the methods, you’ll the ‘@RequestMapping’ annotation. This annotation is used by Spring for routing requests to individual controller methods. It also contains parameters that tell Spring what type data it can produce/consume and what HTTP verb the operation will be executed with. You’ll also see @RequestBody and @ResponseBody. Those tell Spring that the body of the request or response is to be unmarshalled and marshalled, respectively. By default (using the dispatcher-servlet config above), that format is JSON using the Jackson JSON utility. Let’s take a second to look at the value objects we’re mapping the aforementioned JSON to.

Here we have the Gizmo itself. You can see we’re using some annotations here to provide cues for the Jackson Utility. @JsonAutoDetect tells Jackson how to look at individual fields and which fields to look at. In our case, we’re telling Jackson to look only at those fields with a public accessor method.

Depending on the type of response we’re sending we’re also providing a class to wrap a single Gizmo, and multiple Gizmos. You can see in the source there we’re using the same annotations to instruct Jackson where to find the fields it needs to use.

Our read methods are mapped to ‘GET’ at /gizmos. You’ll notice though, that the getFact method also takes a Path Variable using the @PathVariable annotation. Spring allows you to name path variables in your request mapping ({gizmoId} in this case) and bind them to parameters passed to the underlying method (@PathVariable Long gizmoId). Providing the path variable in the request mapping allows Spring to differentiate the two read methods – “/gizmos/1” will return a single gizmo whose id is 1, and “/gizmos” will return all of the gizmos in the database. Our write methods are mapped in much the same way, but you can see the difference in the HTTP methods they work with.

Fire up the app in your favorite container and head to /spring/gizmos, and experiment with the different calls. You can use a nice little Chrome app called Postman to make different types of calls to exercise the whole interface ahead of our ExtJs development.

Good luck and get ready for Part 2 where we add an ExtJs UI on top of what we’ve just developed!

They’re called PathVariables. In my controller, you can see {gizmoId} in the value property of the RequestMapping annotation, and then you can also see it in that method’s input parameters, annotated with @PathVariable, using “gizmoId” as the name. Basically, this tells Spring, while routing your request, to take whatever is in the url at the same location as {gizmoId} and try to pass it into the method as the input parameter named “gizmoId”. Of course, for this binding to take place successfully, you have to ensure that the datatypes are compatible, or supply extra parsing information, as is shown in the spring docs.