Spring@ResponseBody 注释是如何工作的?

我有一个方法,注释如下:

/**
* Provide a list of all accounts.
*/
//  TODO 02: Complete this method.  Add annotations to respond
//  to GET /accounts and return a List<Account> to be converted.
//  Save your work and restart the server.  You should get JSON results when accessing
//  http://localhost:8080/rest-ws/app/accounts
@RequestMapping(value="/orders", method=RequestMethod.GET)
public @ResponseBody List<Account> accountSummary() {
return accountManager.getAllAccounts();
}

所以我知道,通过这个注释:

@RequestMapping(value="/orders", method=RequestMethod.GET)

此方法处理对 URL /命令表示的资源发出的 走开 HTTP 请求。

此方法调用返回 名单的 DAO 对象。

其中 帐户表示系统上的一个用户,并且有一些字段表示这个用户,比如:

public class Account {


@Id
@Column(name = "ID")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long entityId;


@Column(name = "NUMBER")
private String number;


@Column(name = "NAME")
private String name;


@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name = "ACCOUNT_ID")
private Set<Beneficiary> beneficiaries = new HashSet<Beneficiary>();


...............................
...............................
...............................
}

我的问题是: @ResponseBody注释到底是如何工作的?

它位于返回的 List<Account>对象之前,所以我认为它指的是这个 List。课程文档指出,该说明的作用是:

确保结果将通过 HTTP 写入 HTTP 响应 消息转换器(代替 MVC 视图)。

还要阅读官方的 Spring 文档: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

看起来它把 List<Account>对象放到 Http Response中。这是对的还是我误会了?

在之前的 accountSummary()方法的注释中有:

访问时应该得到 JSON 结果 Http://localhost:8080/rest-ws/app/accounts

这到底是什么意思?这是否意味着 accountSummary()方法返回的 List<Account>对象被自动转换成 JSON格式,然后放入 Http Response?不然呢?

如果此断言为真,那么在哪里指定对象将自动转换为 JSON格式?使用 @ResponseBody注释时是否采用标准格式,或者是否在其他地方指定了标准格式?

190695 次浏览

The first basic thing to understand is the difference in architectures.

One end you have the MVC architecture, which is based on your normal web app, using web pages, and the browser makes a request for a page:

Browser <---> Controller <---> Model
|      |
+-View-+

The browser makes a request, the controller (@Controller) gets the model (@Entity), and creates the view (JSP) from the model and the view is returned back to the client. This is the basic web app architecture.

On the other end, you have a RESTful architecture. In this case, there is no View. The Controller only sends back the model (or resource representation, in more RESTful terms). The client can be a JavaScript application, a Java server application, any application in which we expose our REST API to. With this architecture, the client decides what to do with this model. Take for instance Twitter. Twitter as the Web (REST) API, that allows our applications to use its API to get such things as status updates, so that we can use it to put that data in our application. That data will come in some format like JSON.

That being said, when working with Spring MVC, it was first built to handle the basic web application architecture. There are may different method signature flavors that allow a view to be produced from our methods. The method could return a ModelAndView where we explicitly create it, or there are implicit ways where we can return some arbitrary object that gets set into model attributes. But either way, somewhere along the request-response cycle, there will be a view produced.

But when we use @ResponseBody, we are saying that we do not want a view produced. We just want to send the return object as the body, in whatever format we specify. We wouldn't want it to be a serialized Java object (though possible). So yes, it needs to be converted to some other common type (this type is normally dealt with through content negotiation - see link below). Honestly, I don't work much with Spring, though I dabble with it here and there. Normally, I use

@RequestMapping(..., produces = MediaType.APPLICATION_JSON_VALUE)

to set the content type, but maybe JSON is the default. Don't quote me, but if you are getting JSON, and you haven't specified the produces, then maybe it is the default. JSON is not the only format. For instance, the above could easily be sent in XML, but you would need to have the produces to MediaType.APPLICATION_XML_VALUE and I believe you need to configure the HttpMessageConverter for JAXB. As for the JSON MappingJacksonHttpMessageConverter configured, when we have Jackson on the classpath.

I would take some time to learn about Content Negotiation. It's a very important part of REST. It'll help you learn about the different response formats and how to map them to your methods.

First of all, the annotation doesn't annotate List. It annotates the method, just as RequestMapping does. Your code is equivalent to

@RequestMapping(value="/orders", method=RequestMethod.GET)
@ResponseBody
public List<Account> accountSummary() {
return accountManager.getAllAccounts();
}

Now what the annotation means is that the returned value of the method will constitute the body of the HTTP response. Of course, an HTTP response can't contain Java objects. So this list of accounts is transformed to a format suitable for REST applications, typically JSON or XML.

The choice of the format depends on the installed message converters, on the values of the produces attribute of the @RequestMapping annotation, and on the content type that the client accepts (that is available in the HTTP request headers). For example, if the request says it accepts XML, but not JSON, and there is a message converter installed that can transform the list to XML, then XML will be returned.

Further to this, the return type is determined by

  1. What the HTTP Request says it wants - in its Accept header. Try looking at the initial request as see what Accept is set to.

  2. What HttpMessageConverters Spring sets up. Spring MVC will setup converters for XML (using JAXB) and JSON if Jackson libraries are on he classpath.

If there is a choice it picks one - in this example, it happens to be JSON.

This is covered in the course notes. Look for the notes on Message Convertors and Content Negotiation.

@RequestBody annotation binds the HTTPRequest body to the domain object. Spring automatically deserializes incoming HTTP Request to object using HttpMessageConverters. HttpMessageConverter converts body of request to resolve the method argument depending on the content type of the request. Many examples how to use converters https://upcodein.com/search/jc/mg/ResponseBody/page/0