简体   繁体   中英

MessageBody Writer / Reader

I'm trying to make Jersey supports GSON and for this I have read that I need to implement a Custom MessageBodyWriter and MessageBodyReader .

Now my problem is that I couldn't find any clear definition of these 2 interfaces.

From the doc :

public interface MessageBodyWriter<T>

Contract for a provider that supports the conversion of a Java type to a stream. To add a MessageBodyWriter implementation, annotate the implementation class with @Provider. A MessageBodyWriter implementation may be annotated with Produces to restrict the media types for which it will be considered suitable.

and

public interface MessageBodyReader<T>

Contract for a provider that supports the conversion of a stream to a Java type. A MessageBodyReader implementation may be annotated with Consumes to restrict the media types for which it will be considered suitable. Providers implementing MessageBodyReader contract must be either programmatically registered in a JAX-RS runtime or must be annotated with @Provider annotation to be automatically discovered by the JAX-RS runtime during a provider scanning phase.

Can anyone explain me what does concretly means ?

And why do I need to implement them in the case of GSON support for example ?

Thank you.

Contract for a provider...

Just means the set of exposed method by the interfaces. If we implement the interface, we must implement the set of contract methods, for which the framework will use to make use of our implementations

MessageBodyWriter - Contract for a provider that supports the conversion of a Java type to a stream - From our JAX-RS resource methods, we return either a Response with an entity body (which is a Java Object) or a Java object. The message body writer is responsible for converting this Java object to the response's output stream. For example (just for play imagine we don't already have support for JAXB marshalling)

@Override
public void writeTo(Customer c, Class<Customer> type, 
                    Type genericType, Annotation[] annotations, 
                    MediaType mediaType, 
                    MultivaluedMap<String,Object> httpHeaders, 
                    OutputStream entityStream) {

    JAXBContext context = JAXBContext.newInstance(type);
    Marshaller marsaller = context.createMarshaller();
    marshaller.marshal(c, entityStream);
}

You can see that I create the Marshaller , which marshals the Customer object to the provided (by the framework) OutputStream

MessageBodyReader - Contract for a provider that supports the conversion of a stream to a Java type - Same deal as the writer, but this time the process is reversed. Before a Java type is passed to our JAX-RS resource methods, it needs to be converted. For example

@Override
public Customer readFrom(Class<T> type, Type genericType, 
                         Annotation[] annotations, MediaType mediaType,
                         MultivaluedMap<String,String> httpHeaders,
                         InputStream entityStream) throws IOException,
                         WebApplicationException {

    JAXBContext context = JAXBContext.newInstance(Customer.class);
    Unmarshaller unmarshaller = context.createUnmarshaller();

    return (Customer)unarshaller.unmarshal(entityStream);
}

MessageBodyWriter...

To add a MessageBodyWriter implementation, annotate the implementation class with @Provider . A MessageBodyWriter implementation may be annotated with Produces to restrict the media types for which it will be considered suitable

JAX-RS has the concept of Providers . You can just think of it another word for Component . When we annotate our JAX-RS implemented classes with @Provider , it becomes eligable to be component, managed by the framework.

With MessageBodyWriters/MessageBodyReaders , there is a specific algorithm that goes into determining which writer/reader will be used for each request/response. Basically what happens is that JAX-RS calculates a list or writers, based on the @Produces annotations matches. For example if our resource method or resource class is annotated with @Produces(MediaType.APPLICATION_XML) and our provider (writer) is also annotated with this, our writer will be put into this list. Then these providers are sorted by some algorithm. Finally the isWritable method is called on each writer until true is returned by one of them. That is the writer that will be used. For example

@Provider
@Produces(MediaType.APPLICATION_XML)
public class MyJAXBMessageBodyWriter 
                  implements MessaheBodyWriter<Customer> {
    @Override
    public boolean isWriteable(Class<Customer> type, Type genericType, 
                Annotation[] annotations, MediaType mediaType) {

        return type == Customer.class;
    }
}

@GET
@Produces(MediaType.APPLICATION_XML)
public Response getCustomer() {
    Customer customer = customerService.getCustomer();
    return Response.ok(customer).build();
}

These two will be matched, and our writer will be used to convert our Customer object

MessageBodReader...

A MessageBodyReader implementation may be annotated with Consumes to restrict the media types for which it will be considered suitable. Providers implementing MessageBodyReader contract must be either programmatically registered in a JAX-RS runtime or must be annotated with @Provider annotation to be automatically discovered by the JAX-RS runtime during a provider scanning phase.

The @Consumes annotation, same deal as the writer the @Produces , just reversed. Same matching algorithm. Also the @Provider annotation, same deal. For example

@Provider
@Consumes(MediaType.APPLICATION_XML)
public class MyJAXBMessageBodyReader 
                   implements MessageBodyReader<Customer> {
    @Override
    public boolean isReadable(Class<Customer> type, Type genericType, 
                              Annotation[] annotations, MediaType mediaType) {

        return type == Customer.class
    }
}

@POST
@Consumed(MediaType.APPLICATION_XML)
public Response createCustomer(Customer customer) {
    customerService.save(customer);
    return Response.created(newCustomerUri).build();
}

The reader will be matched to our method and convert the input stream into a Customer object and pass it to our method.

As far as the last words "scanning phase" , this is just the JAX_RS implementation scanning our packages looking for components to manage. This happens at startup, based on our configurations (eg what packages should be scanned)

And just for completeness...

MessageBodyWriter has a another method getSize . We can just return -1 if we don't know the size, and the framework will determine the size for us.

…and for some more completeness…

In case you want to programmatically register a MessageBodyReader or MessageBodyWriter , use the register methods on your Client or ClientBuilder (or any Configurable instance).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM