Design + Computation

Java Web Applications with Spring

Matthew Borger ‐ April 24, 2018

I was recently tasked with bringing some new team members up to speed on our usage of the Spring framework and figured my thoughts might be useful to others. My audience came from a C#/.NET background and had no experience with the Java web application ecosystem.

Why Spring?

The first question I got was why Spring? What about Java Enterprise Edition? That sounds more official, better supported, more enterprisy, etc. The simple answer could be, because the team chose Spring at it’s inception, I wasn’t there for that decision, not my fault. A better answer requires understanding how Spring and Java Enterprise Edition solve similar problems in different ways. Some might argue their efforts are wasted duplication until you realize that they evolved concurrently and have spurred innovation through competition.

What’s different about them?

Spring comprises an open source framework and implementation for inversion of control based application containers with extended functionality for implementing web applications. Java Enterprise Edition (JEE) is a collection of specifications for enterprise applications such as web services. The JEE artifacts are generally just interfaces for services such as web request routing, persistence and serialization. The idea being that you write your Java application against the JEE specifications and deploy it on a JEE certified application server which provides the specification implementations. Some popular JEE application servers include Oracle’s GlassFish, Red Hat’s JBoss and IBM’s WebSphere. Spring on the other hand includes an implementation for all of its features and as can be run on any application server that supports the Java Servlet specification. Popular choices for running Spring applications include Tomcat, Jetty and Netty but the previously mentioned application servers can be used as well. Specification wise there is some overlap, over time Spring has adopted support for some of the JEE specifications such as their dependency injection annotations in addition to Spring’s own specification.

So if Spring requires the Servlet specification to work why didn’t they use the rest of the JEE specifications? This is because the Servlet specification predates JEE as a product. Sun published the Servlet specification in 1997. JEE was released alongside Java 1.2 in 1999 which is where the J2EE name came from that many people still use. The included specifications were fairly limited including the Servlet, Beans, JDBC, Transactions and JSP specifications. Early on the only complete implementation was Sun’s WebLogic server which was not open source and required licensing. The open source reference implementation, GlassFish, was not released until 2005. There are not many "certified" application servers that implement all of the JEE specifications. As of 2017, Oracle has transferred stewardship of the JEE project to the Eclipse Foundation which has rebranded it as Jarkarta EE.

Spring was first released as part of a book by Rod Johnson, Expert One-on-One J2EE Design and Development in 2002. Being freely available and open source, the Spring framework gained wide popularity as being less "cumbersome" and a lightweight alternative to JEE. Rodd started a consulting company SpringSource to sponsor development of the Spring Framework, which later merged with Covalent which sponsored development of Tomcat. SpringSource was acquired by VMware in 2009 who later spun out their application oriented products including Spring to Pivotal software in 2013. I feel the team at Pivotal is doing a great job stewarding the project. The documentation has improved greatly from my first interactions and additions like Spring Boot have helped to reduce the boilerplate with opinionated conventions.

So how do you choose which stack to base your product on? It depends on how you envision it being used. If you need your software to co-exist with other products on a shared hosting platform then following the JEE specifications makes more sense. If you’re building an entirely hosted solution or one that doesn’t need to co-exist with other software, then the guarantees of being able to dive into the implementation yourself could steer you to favor Spring. At this point in time their functionality largely overlaps so you’ll need to do your own analysis to determine what’s best for your project.

The bean bag

Both frameworks are based upon an inversion of control application container. An application container is little more than a map of named Java objects, also referred to as Java beans. The Java Beans specification provides annotations for Java classes which the frameworks will instantiate and insert into their application container keyed by a name. This name is often just the class name since many enterprise applications are just a collection of singletons. The container will order the instantiation of the declared beans to satisfy their dependencies following the dependency inversion principle. One of the "beans" in a web application will be a request router which implements the Java Servlet api, javax.servlet.Servlet. This bean will be registered with the web server to handle requests and your application will configure other beans to act as "controllers" to abstract handling certain kinds of requests.

The common approach to defining controllers in Spring is to annotate some Java classes with @Controller. When the Spring framework is initialized it conducts a "component scan" of your code looking for certain annotations. For example the @RequestMapping annotation instructs the Spring router servlet to pass along certain endpoint requests to the annotated method. The http request can be obtained by defining a method parameter with that type and the method just needs to return an appropriate http response object.

Where is the main method?

If this all sounds like magic, you’re not alone. A good starting point to analyzing any codebase is to check out the main method, programming 101. The trouble is there might not be a public static void main method as you’re used to finding in all Java code. Any modern Spring project has a main method which just creates a new SpringApplication, optionally configures, and then runs it. The recommended way of configuring a Spring application is by annotating the class that contains the main method to configure aspects such as component scanning. Older Spring projects were configured entirely through xml. Those applications were started by calling a Spring class with the xml configuration file as an argument.

JEE applications are meant to be entirely managed by the application server. As such you will never find a true main method. You can register call back code for initialization activities but the idea is that the application server can optimize its resources by controlling the lifecycle of your beans.

Diving into a Spring web request

The key to understanding any Java Servlet web application is to root yourself at the Servlet class. In Spring, that is the DispatcherServlet. It serves as the entry point from the web server to field all requests and populate the responses. The Servlet specification provides the concept of an ordered filter chain which can manipulate the request and response objects before and after your controller logic. These filters are part of the web server configuration and are executed outside of the Spring container. Spring has an analogous concept called interceptors which have knowledge about the mapped handler.

cloud {
[HttpRequest]
}

package "Spring" {
[DispatcherServlet]
[RequestMappingHandlerMapping]
[HandlerInterceptors]
[RequestMappingHandlerAdapter]
}

[HttpRequest] --> [WebServer]
note right of [WebServer] : Tomcat, Glassfish, etc
[WebServer] --> [Filters]
note right of [Filters] : Implementations of javax.servlet.Filter
[Filters] --> [DispatcherServlet]
note bottom of [DispatcherServlet] : Implements javax.servlet.Servlet
[DispatcherServlet] -> [RequestMappingHandlerMapping]
[RequestMappingHandlerMapping] -> [HandlerInterceptors]
note bottom of [HandlerInterceptors] : Similar to Filters but have knowledge about your controller
[HandlerInterceptors] -> [RequestMappingHandlerAdapter]

[RequestMappingHandlerAdapter] --> [Controller]

Defining controllers

The key to defining controllers in Spring are the @Controller and @RequestMapping annotations. Your beans are scanned at application initialization for these annotations and registered by the RequestMappingHandlerMapping.

@Controller
class SampleController {

    @RequestMapping(path = "/api/samples", method = GET)
    public Object getSamples() {
        return sampleService.getAll();
    }
}

This controller defines a method which will be called anytime your application receives an HTTP GET request for the path "/api/samples". That request will travel through the entire servlet chain outlined above, enter this method, and return back through the chain to the client. The above controller doesn’t have access to the http request. So what do you do if you want access to it? Just add it as a method parameter and Spring will inject it for you.

@Controller
class SampleController {

    @RequestMapping(path = "/api/samples", method = GET)
    public Object getSamples(HttpServletRequest httpServletRequest) {
        return sampleService.getAll();
    }
}

Spring comes with built-in support for resolving many types of arguments. You can find that list here.

A common need is to pull an argument from the request’s path. This can be accomplished by annotating an argument as a @PathVariable.

@Controller
class SampleController {

    @RequestMapping(path = "/api/samples/{sampleId}", method = GET)
    public Object getSample(@PathVariable long sampleId) {
        return sampleService.get(sampleId);
    }
}

The @PathVariable pattern can be extended to support your own types by registering a Converter with the RequestMappingHandlerAdapter. You can even define and register your own HandlerMethodArgumentResolver to, as an example, extract the last modification date for you from a request header.

@Controller
class SampleController {

    @RequestMapping(path = "/api/samples/{sample}", method = GET)
    public Object getSample(@PathVariable Sample sample, LastModifiedDateTime lastModification) {
        return sample;
    }
}

Making your web service RESTful

The previous controller methods all returned Object which is actually fine. Spring has HandlerMethodReturnValueHandlers which inspect the return value and translate that object into the servlet response. These are configured through the RequestMappingHandlerAdapter. For a "classical" web service that returns HTML, your return value could be a string naming a template to render or a ModelAndView object to provide more metadata during the template rendering process. In order to create a RESTful controller response, you want to have the response object be serialized "as is". This is accomplished by annotating your method with @ResponseBody.

@Controller
class SampleController {

    @ResponseBody
    @RequestMapping(path = "/api/samples/{sample}", method = GET)
    public Sample getSample(@PathVariable Sample sample, LastModifiedDateTime lastModification) {
        return sample;
    }
}

I also change the return type to Sample just to help the developer. Since this is a common pattern, Spring provides a specialization of the @Controller annotation called @RestController which implicitly defines @ResponseBody for every controller method. It helps reduce some boilerplate.

@RestController
class SampleController {

    @RequestMapping(path = "/api/samples/{sample}", method = GET)
    public Sample getSample(@PathVariable Sample sample, LastModifiedDateTime lastModification) {
        return sample;
    }
}

Serializing to JSON

So now your controller method is returning a Sample but your client receives is a JSON string. This return value conversion is handled by the RequestResponseBodyMethodProcessor which is configured with a list of HttpMessageConverter. There are many message converters that use different libraries for serialization. One popular choice for JSON and XML serialization for use with Spring is Jackson and is the default serializer configured. The message converter implementation is MappingJackson2HttpMessageConverter and this is the class that serializes your object into a string and writes it into the servlet response.

These are all of the basic concepts that Spring provides as a web framework. Everything in Spring builds upon these pieces of dependency injection provided by configurable factory classes. The big advantage of this decomposition is that you can consolidate much of your repeated boilerplate logic between your controllers to these factory classes and let Spring execute them at the appropriate time. To learn more about what helpers Spring provides I would thoroughly read through the Web MVC documentation. With the release of Spring 5, the framework provides a Web Reactive stack along with the Web MVC Servlet stack. Everything here refers to the Servlet stack. The Reactive stack is more useful for developing inter-service communication over HTTP.