Spring implementation is based on version 3 with dependency injection. I do not use Spring annotations, but javax.inject instead. I find this approach more flexible, since is not tightly coupled with particular technology.
RESTEasy can be deployed in a various ways – I will concentrate on the most popular one – which is the web application with component scan. At the begining we will start with web 2.4 and later on update to 3.0.
Stand alone deployment, without spring would consists of two main parts – servlet for request processing, bootstrap listener which handles configuration, and finally REST Services itself. Bootstrap listener scans classpath and looks for classes annotated with @Path – those are potential candidates for REST Service. They will be registered in request dispatcher in order to handle incoming HTTP calls. Since this is stand alone deployment, RESTEasy will simply create instances of found REST Services – this means, that those instances and not deployed in any particular container (except tomcat). For example they cannot access Spring context – especially because there is none. But if developer would like to access Spring context within such setup, he would need to manually load Spring context from REST Service class. This is typical approach when accessing Spring from servlet, but there is much better solution:
Let Spring manage services and their dependencies, and RESTEasy handles REST/HTTP related stuff.
In the stand alone example, the REST Services (annotated with @Path) were created by RESTEasy classpath scanner. Now we will use Spring to instantiate REST Services and all dependent components.When the web application starts, as first step the SpringContextLoaderListener is being triggered – it initializes Spring context and all declared components.In the second step, the RESTEasy bootstrap is being triggered – but this is special one, designed for usage with spring. It does not scan classpath for @Path annotated classes – instead it queries Spring for all beans with @Path annotation, and Spring returns references to those beans. This is a big difference, when compared to standalone setup. RESTEasy will not create new instance of REST Service, it will reference beans created by Spring. Basically this is the main difference between standalone deployment and one based on Spring – REST Service instances are created by Spring, so they are running within Spring container, and have access to all Spring components, like any other Spring bean. RESTEasy only forwards incoming calls to those Spring beans.
- Service, component, spring managed bean, REST Service – all are synonyms, and technically they are Plain Java Objects Objects managed by Spring
- Do not instantiate services with new operator. This would create new class outside of Spring context, and such class cannot access Spring manged beans, and therefore injections will not work (will remain null).
- Do not register REST servlet on root context (“/*”), always use some path (“/rest/*”). You might need to add another servlet in the future, and this new servlet will conflict with one registered on root context. You can remove extra path with proper Apache configuration.
This is a web application which exposes simple REST Service. Its implementation forwards incoming calls to Spring component, which handles business logic – in our case it is answer to traditional “Hello World” message.
REST Service exposes two methods:
- GET – /Hello/text – request/response attributes are passed as text in URL. Request has single msg path parameter. Response contains message from request with appended “Hello” text.
- POST – /Hello/javabean – request/response attributes are passed in body as JSON/XML documents. Request contains msg and additionally gender attribute. Response contains message from request with appended “Hello Sir” or “Hello Madam” – depending on provided gender. Response includes also server time, it’s not formatted – this will be covered later.
Project Structure (Maven)
- org.mmiklas.resttutorial.model – POJOs / transfer objects for REST Service
- org.mmiklas.resttutorial.rest – REST Service (Resource) implementation
- org.mmiklas.resttutorial.server – Spring component containing backed implementation
- resources – XML configuration files for logger and Spring
- webapp/WEB-INF – deployment descriptor
Spring configuration consists of single XML file which enables classpath component scan. Dependencies between Spring components are declared by annotations – @Named declares Spring component, and @Inject references existing component.
web.xml has following functions (later on we will update it to 3.0, not its 2.4):
- initializes Spring context from XML file
- initializes RESTEasy and enables Spring integration
- registers RESTEasy dispatcher servlet on “/rest/*” context
Transfer Objects (POJOs)
Transfer objects will be serialized and deserialized from/to JSON. The configuration in both cases is made with JAXB annotations, new JSON parsers are supporting those – but make sure that you have latest version, because it was not always the case.
REST implementation is based only on standard Java API – it does not have Spring or RESTEasy dependencies. This is really good approach to use standardized API as far as possible – it does not matter whenever you are planning to stick with particular framework forever – clean standardized code is easier to read.
@Named annotation defines HelloRestService class as Spring component, and @Path declares this class additionally as REST Service provider. Spring will create instance of our class, and RESTEasy will expose its methods over HTTP.
sayTextHello accepts GET messages on …./Hallo/text and reads query parameter msg.
sayJavaBeanHello accepts POST messages on …./Hallo/javabean and supports XML and JSON documents. XML is supported by default, since Java contains required providers (marshaller/unmarshaller). JSON must be enabled – since it’s not JDK part – I will describe that in next chapter.
Spring Component (Backend)
Spring implementation is self explanatory – service is declared with @Named. @Componentwould work as well, but it’s not standarized annotation.
sayJavaBeanHello REST method supports JSON media type. In order to enable it, we only need to have proper jar in classpath:
RESTEasy scans during bootstrap phase classpath and searches for content providers – they are divided into two groups – marshaller and unmarshaller:
- unmarshaller class in annotated with @Provider, @Consumes and implementsMessageBodyReader interface
- marshaller class in annotated with @Provider, @Produces and implementsMessageBodyWriter interface
@Provider annotation is only marker declaring, that given class has some interesting functionality – first the implementing interface specifies it – annotation alone is not sufficient.@Produces and @Consumes are defining mime type which is supported by our provider – the same annotations can be found on rest methods.
Both interfaces above have generic type (T), which is currently set to Object – it provides possibility to register dedicated provider for the same mime type, but different class type. For example we can register different provider for User and Group and both for mime type JSON.