Document directory
- Code snippets in this article
- Create spring-managed JAX-RS resources in Jersey
- Create spring MVC @ controller class
- Scope of web layer components
- Request Parameters, cookies, and HTTP headers
- Process data in the Request body
- Set response body data with JAX-RS
- Use spring MVC to set response body data
- Status Code and Response Header
- Exception Handling
Http://www.infoq.com/cn/articles/springmvc_jsx-rs
Over the past few years, rest has gradually become an important concept that affects the design of web frameworks, web protocols, and Web applications. If you are not familiar with rest, this brief introduction will help you quickly master rest. You can also click here to learn more about rest.
Nowadays, more and more companies want to publish Web APIs in a simple and adaptive way. It is not surprising that rest is becoming more and more important. Rich browsers that use Ajax for communication are also moving towards this goal. This architecture principle improves the scalability of the World Wide Web, and can benefit from any application.
JAX-RS (JSR 311) refers to Java API for restful Web Services, Roy Fielding is also involved in the formulation of the JAX-RS, he defined rest in his doctoral thesis. For developers who want to build restful Web Services, JAX-RS offers a different solution than JAX-WS. At present there are four kinds of JAX-RS implementation, all these implementations support spring, Jersey is the reference implementation of JAX-RS, is also the implementation of this article.
If you use spring for development, you may want to know (or someone has asked you) What are the similarities and differences between spring MVC and JAX-RS? Furthermore, if you have a spring MVC application that uses the control class inheritance (simpleformcontroller, etc.), you may not realize the extensive support of spring MVC for rest.
This article will introduce the rest feature in Spring 3 and compare it with the JAX-RS, hoping to help you straighten out the similarities and differences between the two programming models.
Before the beginning, it is necessary to point out that the goal of JAX-RS is Web Services Development (which is different from HTML Web applications) and spring MVC is web application development. Spring 3 provides extensive rest support for web applications and web services, but this article focuses on the features related to Web services development. I think this approach is more helpful for discussing spring MVC in the context of a JAX-RS.
The second point to note is that the rest feature we will discuss is part of the Spring framework and is a continuation of the existing spring MVC programming model. Therefore, there is no such concept as "Spring rest framework", and some are just spring and spring MVC. This means that if you have a spring application, you can use spring MVC to create an HTML web layer or a restful Web Services layer.
Code snippets in this article
The code snippet in this article assumes a simple domain model: Two JPA annotation entities, account and portfolio, one of which corresponds to multiple portfolio. The persistence layer uses spring configurations and contains a JPA warehousing implementation for obtaining and persisting entity instances. Jersey and spring MVC are used to construct the Web Services Layer and serve client requests by calling the underlying spring-hosted application.
Bootstrap program and web layer packaging
We will use spring to implement dependency injection in spring MVC and JAX-RS. Spring MVC dispatcherservlet and Jersey springservlet will delegate requests to the rest layer components (controllers or resources) managed by spring. The latter will be packaged by the business or persistence layer components, as shown in:
Jersey and spring MVC both use spring's contextloaderlistener to load business and persistence layer components, such as jpaaccountrepository:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:META-INF/spring/module-config.xml </param-value></context-param><listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class></listener>
Contextloaderlistener can be used in any web or rest framework environment.
Create spring-managed JAX-RS resources in Jersey
Jersey supports using spring in the rest layer, which can be done in two simple steps (in fact, three steps also need to be added to Maven artifact COM. sun. jersey. contribs: in Jersey-spring ).
Step 1: Add the following configuration snippets to Web. XML to ensure spring can create JAX-RS root resources:
<servlet> <servlet-name>Jersey Web Application</servlet-name> <servlet-class> com.sun.jersey.spi.spring.container.servlet.SpringServlet </servlet-class></servlet><servlet-mapping> <servlet-name>Jersey Web Application</servlet-name> <url-pattern>/resources/*</url-pattern></servlet-mapping>
Step 2: Use spring and JAX-RS annotations to declare the root JAX-RS Resource class:
@Path("/accounts/")@Component@Scope("prototype")public class AccountResource { @Context UriInfo uriInfo; @Autowired private AccountRepository accountRepository;}
These annotations are described as follows:
@ Component declare accountresource as spring bean.
@ Scope declares a prototype spring bean, which will be instantiated (for example, every request) during each use ).
@ Autowired specifies an accountrepository reference, which is provided by spring.
@ Path is a JAX-RS annotation that declares accountresource as a root JAX-RS resource.
@ Context is also a JAX-RS annotation that requires injection of the request-specific uriinfo object.
JAX-RS has the concepts of "root" Resources (marked as @ path) and sub-resources. In the preceding example, accountresource is a root resource and processes the path starting with "/accounts. For methods in accountresource, such as getaccount (), you only need to declare the relative path for the Type level.
@Path("/accounts/")@Component@Scope("prototype")public class AccountResource { @GET @Path("{username}") public Account getAccount(@PathParam("username") String username) { }}
The request for the access path "/accounts/{username}" (where username is the path parameter, which can be the user name of an account) will be processed by the getaccount () method.
The root resource is instantiated by the JAX-RS Runtime (spring in this example), and the Child resource is instantiated by the application itself. For example, for requests such as "/accounts/{username}/portfolios/{portfolioname}", accountresource (identified by the first part of the path "/Accounts) A sub-resource instance is created, and the request is forwarded to the instance:
@Path("/accounts/")@Component@Scope("prototype")public class AccountResource { @Path("{username}/portfolios/") public PortfolioResource getPortfolioResource(@PathParam("username") String username) { return new PortfolioResource(accountRepository, username, uriInfo); }}
The declaration of portfolioresource itself does not use annotations, so all its dependencies are transmitted by the parent resource:
public class PortfolioResource { private AccountRepository accountRepository; private String username; private UriInfo uriInfo; public PortfolioResource(AccountRepository accountRepository, String username, UriInfo uriInfo) { this.accountRepository = accountRepository; this.username = username; this.uriInfo = uriInfo; }}
The root and sub-resource in the JAX-RS creates a processing chain that calls multiple resources:
Remember that the resource class is a web services layer component and should focus on web services-related processing, such as input conversion, response preparation, and response code setting. In addition, the practice of separating the Web Services logic from the business logic requires that the business logic be packaged into a separate method as the transaction boundary.
Create spring MVC @ controller class
For spring MVC, we need to create dispatcherservlet and specify the contextconfiglocation parameter as spring MVC Configuration:
<servlet> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/*.xml </param-value> </init-param></servlet>
A small amount of configuration is required to use annotation-based programming models in spring MVC (@ MVC. The following component-scan element tells spring where to find the @ controller annotation class.
<context:component-scan base-package="org.springframework.samples.stocks" />
Next, we declare accountcontroller, as shown in the following code:
@Controller@RequestMapping("/accounts")public class AccountController { @Autowired private AccountRepository accountRepository;}
The @ requestmapping annotation maps the Controller to all requests starting with "/Accounts. For methods in accountcontroller, such as getaccount (), you only need to declare the relative address for "/Accounts.
@RequestMapping(value = "/{username}", method = GET)public Account getAccount(@PathVariable String username) {}
Spring MVC does not have the concept of root resources and sub-resources, so that each controller is managed by spring rather than applications:
@Controller@RequestMapping("/accounts/{username}/portfolios")public class PortfolioController { @Autowired private AccountRepository accountRepository;}
Requests for "/accounts/{username}/portfolios" will be directly forwarded to portfoliocontroller, and accountcontroller will not be involved at all. Note that the request can also be directly processed by accountcontroller, so that portfoliocontroller is not required.
Scope of web layer components
In the JAX-RS, accountresource is declared through the pre-Request (Per-Request) semantics, which is also the default recommendation setting for the JAX-RS. This can inject request-specific data and store it into the resource class itself, which applies to the root-level resources managed by the JAX-RS. Sub-resources are instantiated by applications and will not directly benefit from this method.
In spring MVC, controllers are always single-sample, and they use request-specific data as method parameters. The JAX-RS can also do this by creating resources in a singleton way.
Map requests to methods
Next let's look at how spring MVC and JAX-RS map requests to methods. Both @ path and @ requestmapping can extract the PATH variable from the URL:
@Path("/accounts/{username}")@RequestMapping("/accounts/{username}")
Both frameworks can also use regular expressions to extract PATH variables:
@Path("/accounts/{username:.*}")@RequestMapping("/accounts/{username:.*}"
Spring MVC's @ requestmapping can match the request based on the query parameter:
@RequestMapping(parameters="foo")@RequestMapping(parameters="!foo")
Or match based on the query parameter values:
@RequestMapping(parameters="foo=123")
@ Requestmapping can also match requests based on the header information:
@RequestMapping(headers="Foo-Header")@RequestMapping(headers="!Foo-Header")
Or match based on the value of the header information:
@RequestMapping(headers="content-type=text/*")
Process request data
The HTTP request contains the data that the application needs to extract and process, such as HTTP headers, cookies, query string parameters, form parameters, and a large amount of data contained in the request body (such as XML and JSON. (. Pdf) specifies the content type and so on. Httpservletrequest provides all the underlying access mechanisms to handle all of this, but it is boring to directly use httpservletrequest.
Request Parameters, cookies, and HTTP headers
Spring MVC and JAX-RS have annotations that can extract this HTTP request value:
@GET @Pathpublic void foo(@QueryParam("q") String q, @FormParam("f") String f, @CookieParam("c") String c, @HeaderParam("h") String h, @MatrixParam("m") m) { // JAX-RS}@RequestMapping(method=GET)public void foo(@RequestParam("q") String q, @CookieValue("c") String c, @RequestHeader("h") String h) { // Spring MVC}
The above annotation is very like, the difference is that the JAX-RS supports the extraction of matrix parameters (matrix parameters), has a separate annotation to process the query string and form parameters. Matrix parameters are not common. They are similar to query string parameters, but use special path fragments (such as get/images; name = Foo; type = GIF ). We will introduce the form parameters later.
If a pre-request range is used to declare a resource, the JAX-RS can use the preceding Annotation on properties and setters methods.
Spring MVC has a feature that allows us to omit a few characters. If the annotation name is the same as the Java parameter name, we can omit the above annotation name. For example, the request parameter named "Q" requires that the method parameter also be "Q ":
public void foo(@RequestParam String q, @CookieValue c, @RequestHeader h) {}
This is too convenient for situations where the method signature becomes longer due to the annotation used in the parameter. Remember that this feature requires the code to be compiled using debugging symbols.
Type conversion and formatting of HTTP request values
The HTTP request value (header, cookies, and parameters) is a constant string and needs to be parsed.
The JAX-RS parses request data by looking for the valueof () method or by receiving a string constructor in a custom target type. JAX-RS supports the following types of annotation method parameters, including PATH variables, request parameters, HTTP header values, and cookies:
- Native type.
- Type of constructor that receives a single string parameter.
- It has a type of static method named valueof that receives a single string parameter.
- List <t>, set <t>, or sortedset <t>, where T meets the preceding two or three requirements.
Spring 3 supports all the above requirements. In addition, spring 3 provides a new type conversion and formatting mechanism, and can be implemented using annotations.
Form data
As mentioned above, the JAX-RS processes query string parameters differently from form parameters. Although spring MVC only has one @ requestparam, it also provides a data binding mechanism familiar to spring MVC users to process form input.
For example, if a form submits three pieces of data, one possible way is to declare a method with three parameters:
@RequestMapping(method=POST)public void foo(@RequestParam String name, @RequestParam creditCardNumber, @RequestParam expirationDate) { Credit card = new CreditCard(); card.setName(name); card.setCreditCardNumber(creditCardNumber); card.setExpirationDate(expirationDate);}
However, as the amount of data in the form increases, this processing method becomes impractical. With data binding, spring MVC can create, assemble, and pass form objects containing nested data (Bill address, email address, etc.) and any structure.
@RequestMapping(method=POST)public void foo(CreditCard creditCard) { // POST /creditcard/1 //name=Bond //creditCardNumber=1234123412341234 //expiration=12-12-2012}
To work with Web browsers, form processing is an important part. On the other hand, the Web Services Client generally submits XML or JSON data in the Request body.
Process data in the Request body
Both spring MVC and JAX-RS can automatically process data in the Request body:
@POSTpublic Response createAccount(Account account) { // JAX_RS}@RequestMapping(method=POST)public void createAccount(@RequestBody Account account) { // Spring MVC}
Request body data in JAX-RS
In the JAX-RS, the entity provider of the messagebodyreader type is responsible for converting the Request body data. The implementation of the JAX-RS requires a jaxb messagebodyreader, which can be implemented using a custom messagebodyreader with annotation @ provider.
Request body data in spring MVC
In spring MVC, if you want to initialize the method parameters through the request body data, you can add the @ requestbody annotation to the method parameters, which is the opposite of the previously introduced form parameter initialization.
In spring MVC, The httpmessageconverter class is responsible for converting Request body data. Spring MVC provides an out-of-the-box spring oxm httpmessageconverter. It supports jaxb, Castor, jibx, xmlbeans, and xstream, and a Jackson httpmessageconverter for processing JSON.
Httpmessageconverter registers with annotationmethodhandleradapter, which maps incoming requests to spring MVC @ controllers. The configuration is as follows:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" > <property name="messageConverters" ref="marshallingConverter"/></bean><bean id="marshallingConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"> <constructor-arg ref="jaxb2Marshaller"/> <property name="supportedMediaTypes" value="application/vnd.stocks+xml"/></bean><oxm:jaxb2-marshaller id="jaxb2Marshaller"/>
This configuration is described as follows:
The newly added MVC custom namespace in Spring 3 automates the above configuration. You only need to add the following configuration snippets:
<mvc:annotation-driven />
If jaxb is located in the class path, it registers a converter for reading and writing XML; if Jackson is located in the class path, it registers a converter for reading and writing JSON.
Prepare response
For a typical response, you need to prepare the response code, set the HTTP response header, and put the data in the response body. You also need to handle exceptions.
Set response body data with JAX-RS
In the JAX-RS, to add data to the response body, you only need to return the object from the resource method:
@GET@Path("{username}")public Account getAccount(@PathParam("username") String username) { return accountRepository.findAccountByUsername(username);}
The JAX-RS looks for the entity provider of the messagebodywriter type, which can convert the object to the desired content type. The JAX-RS implementation requires a jaxb messagebodywriter, which can be implemented using a custom messagebodywriter with annotation @ provider.
Use spring MVC to set response body data
In spring MVC, the response is implemented through a view parsing process, which can be selected from a series of view technologies. However, when interacting with the Web Services Client, a more reasonable way is to discard the view parsing process and use the objects returned by the method instead:
@RequestMapping(value="/{username}", method=GET)public @ResponseBody Account getAccount(@PathVariable String username) { return accountRepository.findAccountByUsername(username);}
If the @ responsebody annotation is applied to the Controller method or its return type, httpmessageconverter is used to process the returned value, and then the response body is set with the returned value. The httpmessageconverter set used for the request body parameter is also used for the response body, so no configuration is required.
Status Code and Response Header
The JAX-RS uses a chained API to build a response:
@PUT @Path("{username}")public Response updateAccount(Account account) { // ... return Response.noContent().build();// 204 (No Content)}
This can be used together with uribuilder to create an object link for the location Response Header:
@POSTpublic Response createAccount(Account account) { // ... URI accountLocation = uriInfo.getAbsolutePathBuilder().path(account.getUsername()).build(); return Response.created(accountLocation).build();}
The uriinfo used in the code above is either injected into the root Resource (using @ context) or transmitted from the parent resource to the Child resource. It can be appended to the path of the current request.
Spring MVC provides an annotation to set the response code:
@RequestMapping(method=PUT)@ResponseStatus(HttpStatus.NO_CONTENT)public void updateAccount(@RequestBody Account account) { // ...}
You can directly use the httpservletresponse object to set the location header:
@RequestMapping(method=POST)@ResponseStatus(CREATED)public void createAccount(@RequestBody Account account, HttpServletRequest request,HttpServletResponse response) { // ... String requestUrl = request.getRequestURL().toString(); URI uri = new UriTemplate("{requestUrl}/{username}").expand(requestUrl, account.getUsername()); response.setHeader("Location", uri.toASCIIString());}
Exception Handling
The JAX-RS allows the resource method to throw a webapplicationexception type exception that contains a response. The following sample code converts a JPA noresultexception to a notfoundexception specific to Jersey, resulting in a 404 error:
@GET@Path("{username}")public Account getAccount(@PathParam("username") String username) { try { return accountRepository.findAccountByUsername(username); } catch (NoResultException e) { throw new NotFoundException(); }}
The webapplicationexception instance encapsulates the necessary logic to generate a specific response, but exceptions must be caught in each independent resource class method.
Spring MVC supports defining controller-level methods to handle exceptions:
@Controller@RequestMapping("/accounts")public class AccountController { @ResponseStatus(NOT_FOUND) @ExceptionHandler({NoResultException.class}) public void handle() { // ... }}
If any controller method throws a JPA noresultexception exception, the above processor method will get a call and handle the exception, and then return a 404 error. In this way, each controller can handle exceptions, as if from the same place.
Summary
I hope this article will help you understand how spring MVC builds restful Web Services and its similarities and differences with JAX-RS programming models.
If you are a spring MVC user, you may have used it to develop HTML Web applications. The concept of rest applies to Web Services and Web applications, especially rich client interactions. In addition to the features described in this article, spring 3 also provides support for restful web applications. This is a list of some new features: create a new JSP custom tag for the URL from the URL template, simulate the servlet filter for form submission based on http put and delete, automatically select the contenttypenegotiatingviewresolver for the view based on the content type, and implement the new view. In addition, the spring document has been improved a lot.
About the author
Rossen stoyanchev is a senior consultant at springsource. In his career, he has worked in trade applications, accounting systems, e-commerce and other Web applications. In springsource, rossen focuses on web technologies, including consulting, training, and content development for the rich-web development with Spring course, this course aims to help Trainees become certified spring web application developers.
View Original English text:A comparison of spring MVC and JAX-RS