Header-53.jpg

INSIGHTS

EJB3 vs. Spring: Rest Services

 

Which framework is better?

If you anyone has ever asked you if you prefer pizza or ice cream, you probably answered “Well… it depends”.  While frustrating, this is often the correct answer when asking which software framework is “better”.  So it is when comparing the Spring Framework with the capabilities provided by an EJB3 container.  I have decided to start exploring this question myself, in particular the functionality where EJB3 and the various Spring modules overlap.

Clearly this is not a small task, particularly since I am not intimately familiar with all the possibilities offered by the Spring framework.  To ease my pain, I am going to break this analysis up into a series of posts, each building upon the last.  It is unlikely that any of these posts will be truly exhaustive in content, but I will do my best to represent the “80%”. 

Getting started

I will keep my code as platform independent as I am able.  Knowing how minor nuances in containers can affect the behavior of deployed Enterprise Applications, I will divulge that I am deploying my application to JBoss EAP 6.3 for testing.  If I do happen to include any JBoss proprietary deployment descriptors or configurations, I will call them out and explain the approach.

The Framework Comparison Application (FWC) is a maven project organized into several modules.  All of the source code is available for your perusal on github at https://github.com/jgitter/fwc.  The modules are as follows:

  • fwc: The parent POM module
  • fwc-ear: The EAR enterprise application module
  • fwc-common: Java module that contains common code such as interfaces, exceptions and transfer objects
  • fwc-dao: EJB module containing the DAO layer, accessible to all web applications deployed to the EAR
  • fwc-web: WAR module that contains the front-end application used for illustration
  • fwc-ejb-web: WAR module that contains the EJB services.  The intent is to encapsulate the EJB3-specific implementations for the purposes of comparison
    • fwc-ejb-services: EJB module that implements the actual EJB services
  • fwc-spring-web: WAR module that contains the Spring services and Spring framework libs.  The intent is to encapsulate the Spring-specific implementations for the purposes of comparison
    • fwc-spring-services: Java module that implements the actual Spring services

 

EJB3 and Spring Configuration

One of the myths that seems to persist is the idea that EJB3 uses primarily annotation-based configuration and is therefore, somehow, superior to Spring which uses primarily XML-based configuration.  There are two fallacies in this statement that I would like to overcome.

The first fallacy is that annotations are superior to XML for configuration, or vice versa.  In fact, it depends highly upon how they are being used, what they are configuring, and ultimately, some level of aesthetics.  For instance, I personally think that transaction management, cross-cutting concerns, or web service endpoint configuration is perfect fit for configuration via annotations.  It provides clear definition of behavior, is self-documenting, and easy to change.  Furthermore, these types of configurations should not be subject to change lightly and may require a redesign of a system, or part of a system.  On the other hand, I believe JPA mapping configuration is a great fit for XML configuration.  It keeps all traces of your data sources out of your code allowing you to cleanly separate the two distinct activities of data mapping and data access.

Second, it isn’t true that either EJB3 or Spring lends itself to be primarily one or the other.  Both offer options for most configurations to be handled via either XML or annotation.  I will attempt to show both options for configuration in my service examples.

Restful EJB3 Service

In order for a Restful JAX-RS service to work, we need to map the javax.ws.rs.core.Application to a URL pattern.  There are two ways to achieve this.  The first method is via a servlet-mapping definition in your deployment descriptor (web.xml) and should look pretty familiar.

 

XML Configuration via web.xml:

<web-app>
    <!-- snip -->
    <servlet>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

The second way to achieve the same result is to use the @ApplicationPath annotation on a POJO.  This annotation is discovered upon startup and the container will map the Application servlet for you. 

 

Java Configuration via annotation:

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class FwcApplication extends Application {}

 

Note that the path is "/rest" and not "/rest/*" as in the web.xml above.  These two configurations are equivalent and result in all calls to /<context-root>/rest/* to be routed through the JAX-RS Application servlet.

There are additional options and behaviors that can be configured but I will not cover those here.  This is enough to give us basic JAX-RS services.  Here is the EJB3 web service implementation:

 

The EJB3 service implementation:

@Path("/institution")
@Stateless
public class InstitutionRestController {

    @Inject
    private InstitutionService service;

    @GET
    @Path("{keyword}")
    @Produces(MediaType.APPLICATION_JSON)
    public List<Map<String, String>> findInstitutions(@PathParam("keyword") String keyword) {
        return service.findInstitutions(keyword);
    }
}

There are a few things happening here.

  1. The @Path annotation provides the relative path to make calls to the exposed methods of this restful service.  Notice that annotation is on the class as well as the method itself.  For this example, the path to the findInstitutions service method is /<context_root>/fwc-ejb/rest/institution/{keyword} where \{keyword\} is any non-null string containing a search term.
  2. The @Inject is a CDI annotation.  Dependency Injection is not the subject of this article, so I will just say that this annotation can be used to inject any CDI bean into a container-managed resource.  In this case, InstitutionService is a POJO decorated with the CDI annotation @RequestScoped.  See the CDI documentation for more information on the subject.
  3. The @GET annotation informs the JAX-RS implementation that this method can only be accessed via the HTTP GET method.
  4. The @Produces annotation informs the JAX-RS implementation that this method will result in a response that contains JSON data.  JBoss will uses Resteasy under the hood, which will transform the returned object to JSON.
  5. The @PathParam annotation informs the JAX-RS implementation that the keyword parameter should be parsed from the URL path wherever the \{keyword\} path is configured.

And that's all there is to it.  This is enough to expose a simple Restful web service.  I did not mention the @Stateless annotation, which marks this service as a Stateless EJB3 Session Bean.  This is not necessary to expose this as a service, but comes with a few benefits.  For instance, the container will create a pool of reusable service objects for handling requests, enables injection since the bean is container-managed, and exposes the bean through JNDI for local service calls.  Simply adding an @Remote interface would also expose it for remote invocation.

Restful Spring Service

Configuring a container for Spring Web MVC Restful services is very similar to the previous example.  The first step is to map the Spring DispatcherServlet, which acts as the top-level controller for your services, much as the Application servlet did for the JAX-RS implementation.  This is done via the deployment descriptor (web.xml).

 

Spring DispatcherServlet mapping:

 <web-app>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

 

This will map all requests to /<context_root>/rest/* through the DispatcherServlet.  Unlike the previous example, we aren't done configuring the container here since the Spring container still needs to be made aware of your service controllers.  There are a number of ways to do this, however the easiest way to handle this is by updating your the configuration for your context.  The ContextLoaderListener will bootstrap your application context upon startup of the container which will look for the applicationContext.xml.  You can configure this here.

 

applicationContext.xml:

<beans>
    <mvc:annotation-driven />
    <context:component-scan base-package="org.gitter.fwc" />
    <!-- snip -->
</beans>

 

This can be a little deceptive as there is a lot going on here. The <mvc:annotation-driven> tag enables annotation configuration for your Restful Controllers via the @Controller, @RestController, @RequestMapping, and @ExceptionHandler annotations and provides the standard conversion service for serializing your data for transfer.  It also sets up the HttpMessageConverter which takes care of your @RequestBody and @ResponseBody annotations.  In short, one line turns on all of your Spring Web MVC annotations.  Alternatively I could leave this off and configure my entire controller via xml, however I consider that configuration to be somewhat tedious and counter-productive, so I'm not going to show it here.

The <context:component-scan> tag activates all of your Spring beans, as well as scans the package indicated for any bean annotations to be registered automatically, such as @Component, @Service, @Repository, etc.  This is a shortcut that allows you to skip the tedious, manual creation of xml configuration for registering simple beans.  For example, I don't need to register my InstitutionService.

 

 Not Needed:

<beans>
    <!-- snip -->
    <bean name="institutionService" class="org.gitter.fwc.InstitutionService">
        <property name="dao" ref="institutionDAO" />
    </bean>
</beans>

As promised, here is an annotation configuration alternative to the applicationContext.xml configuration above.

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "org.gitter.fwc" })
public class WebMvcConfiguration {}

Now that everything is configured, we just need a service implementation. 

 

Spring Rest Service Implementation:

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/institution")
public class InstitutionRestController {

    @Autowired
    private InstitutionService service;

    @RequestMapping(value = "/{keyword}", method = RequestMethod.GET)
    public @ResponseBody List<Map<String, String>> find(@PathVariable("keyword") String keyword) {
        return service.findByKeyword(keyword);
    }
}

 

Here's the breakdown

  1. The @Controller annotation marks it as a Web MVC Controller and is picked up by the context component scan so it can be registered as a Spring bean.
  2. The @RequestMapping annotation allows us to set a URL mapping to our web methods.  This is analogous to the @Path annotation in JAX-RS.  In addition, we are mapping the GET method via the method attribute of this annotation, as opposed to the @GET annotation in JAX-RS.
  3. The @Autowired annotation is used to inject one Spring resource into another.  Again, Dependency Injection is not the subject of this article, so I won't go into detail.  However, my InstitutionService is marked as a @Component so it was picked up by the context component scan and registered as a spring bean, enabling it for auto wired injection.
  4. The @ResponseBody annotation activates the HttpMessageConverter when encountered during a service method call to convert the returned object to an HTTP response.  Alternatively, as of Spring 4, you can put @ResponseBody on the class level, or use @RestController instead of @Controller.  Either of these will cause the HttpMessageConverter to be invoked for all of the service methods so you don't have to annotate each method.
  5. The @PathVariable annotation is analogous to the @PathParam JAX-RS annotation and simply maps the parameter to a URL path variable named "{keyword}".

Analysis

One topic I did not cover is the customization for handling of request and response objects.  However, most JAX-RS implementations and Spring will use 3rd party libraries for marshaling and unmarshaling that data anyway.  Suffice to say, it is possible to do this using either method through fairly simple configuration so I'm not going to go into detail for sake of brevity.

Another topic not addressed in the examples is that of bean scope.  It can be advantageous to scope a bean's lifecycle to a user's HTTP Session, instead of to the request itself.  The ubiquitous example is the shopping cart.  You don't want to have to recreate the entire cart each time the user adds an item, nor do you want to have to serialize and pass that entire cart back and forth from server to client or store it on the client.  There are many ways to preserve the state of this cart, but one easy way to handle this is through the use of a bean scoped to the HTTP Session.  Again, there is no clear advantage.  Spring offers the ability to scope your beans via the scope attribute of the bean tag in xml or via the @Scope annotation on your beans, though you must use a web-aware application context such as the XmlWebApplicationContext or ClassPathXmlApplicationContext.  While EJB 3 itself does not do this, you can achieve this through the use of the @SessionScoped annotation from CDI on CDI beans which can be injected into your services.

So which is better?  I set out to elaborate upon the cliché "it depends" answer, but I have ironically arrived back at the same conclusion.  In short, while Spring Web MVC is easy to learn and even fun to use it offers no clear advantage.  It is my opinion that you don't choose to build a web application with Spring to get Web MVC.  However, if you've already chosen to use Spring and want to add restful web services, Web MVC is probably the way to go.  If you're adding Spring to an existing application with restful web services, you may actually be better served to hook the existing framework (e.g. Resteasy or Jersey) into the Spring Web module instead of reconfiguring everything for Web MVC.  This idea is discussed in Spring's documentation at http://docs.spring.io.

 

 

EJB3 VS. SPRING Series:

REST Services (Above)

Simple SOAP Services

Topics: Architecture & Design, Java, Open Source, Software Development, Microservice

Written by Jeff Gitter

Leave a Comment