第 51 章 实体支持
摘要
Apache CXF 运行时支持 MIME 类型和 Java 对象之间的有限数量的映射。开发人员可以通过实施自定义读取器和写入器来扩展映射。自定义读取器和写入器在启动时使用运行时注册。
概述
运行时依赖于 JAX-RS MessageBodyReader 和 MessageBodyWriter 实现来序列化 HTTP 消息及其 Java 表示之间的数据。读取器和写入器可以限制他们能够处理的 MIME 类型。
运行时为多个常见映射提供读取器和写入器。如果应用程序需要更多的映射,开发人员可以提供 MessageBodyReader 接口和/或 MessageBodyWriter 接口的自定义实现。自定义读取器和写入器在应用启动时使用运行时注册。
原生支持的类型
表 51.1 “原生支持的实体映射” 列出 Apache CXF 提供的实体映射。
Java 类型 | MIME 类型 |
---|---|
原语类型 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JAXB 注解的对象 |
|
javax.ws.rs.core.MultivaluedMap<String, String> |
|
javax.ws.rs.core.StreamingOutput |
|
[a]
此映射用于处理 HTML 表单数据。
[b]
这个映射只支持将数据返回到消费者。
|
自定义读取器
自定义实体读取器负责将传入的 HTTP 请求映射到服务实施可以操作的 Java 类型中。它们实施 javax.ws.rs.ext.MessageBodyReader 接口。
例 51.1 “消息读取器接口” 中显示的接口有两个需要实现的方法:
例 51.1. 消息读取器接口
package javax.ws.rs.ext; public interface MessageBodyReader<T> { public boolean isReadable(java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType); public T readFrom(java.lang.Class<T> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType, javax.ws.rs.core.MultivaluedMap<String, String> httpHeaders, java.io.InputStream entityStream) throws java.io.IOException, WebApplicationException; }
isReadable()
isReadable ()
方法决定了读取器是否能够读取数据流,并创建正确的实体表示类型。如果读取器可以创建正确的实体类型,则方法返回为true
。表 51.2 “用于确定读取器是否可以生成实体的参数” 描述
isReadable ()
方法的参数。表 51.2. 用于确定读取器是否可以生成实体的参数 参数 类型 描述 type
类<T>
指定用于存储实体的对象的实际 Java 类。
genericType
类型
指定用于存储实体的对象的 Java 类型。例如,如果要将消息正文转换为 method 参数,则该值将是
Method.getGenericParameterTypes ()
方法返回的 method 参数的类型。annotations
Annotation[]
指定为存储实体而创建的对象声明的注解列表。例如,如果消息正文要转换为 method 参数,这将是
Method.getParameterAnnotations ()
方法返回的该参数上的注解。mediaType
MediatType
指定 HTTP 实体的 MIME 类型。
readFrom()
readFrom ()
方法读取 HTTP 实体,并将其覆盖到所需的 Java 对象中。如果读取成功,则方法返回包含该实体的创建的 Java 对象。如果在读取输入流时发生错误,方法应抛出 IOException 异常。如果发生错误需要 HTTP 错误响应,应抛出带有 HTTP 响应的 WebApplicationException。表 51.3 “用于读取实体的参数” 描述
readFrom ()
方法的参数。表 51.3. 用于读取实体的参数 参数 类型 描述 type
类<T>
指定用于存储实体的对象的实际 Java 类。
genericType
类型
指定用于存储实体的对象的 Java 类型。例如,如果要将消息正文转换为 method 参数,则该值将是
Method.getGenericParameterTypes ()
方法返回的 method 参数的类型。annotations
Annotation[]
指定为存储实体而创建的对象声明的注解列表。例如,如果消息正文要转换为 method 参数,这将是
Method.getParameterAnnotations ()
方法返回的该参数上的注解。mediaType
MediatType
指定 HTTP 实体的 MIME 类型。
httpHeaders
MultivaluedMap<String, String>
指定与实体关联的 HTTP 消息标头。
entityStream
InputStream
指定包含 HTTP 实体的输入流。
重要这个方法不应关闭输入流。
在 MessageBodyReader 实现可用作实体阅读器之前,它必须使用 javax.ws.rs.ext.Provider
注解进行分离。@Provider
注释提醒提供的实施提供了额外功能的运行时。这个实现也必须使用运行时注册,如 “注册阅读器和作者”一节 所述。
默认情况下,自定义实体供应商处理所有 MIME 类型。您可以使用 javax.ws.rs.Consumes
注解限制自定义实体读取器将处理的 MIME 类型。@Consumes
注释指定自定义实体提供程序读取的、以逗号分隔的 MIME 类型列表。如果实体不是指定的 MIME 类型,则不会选择实体提供程序作为可能的读取器。
例 51.2 “XML 源实体读取器” 显示使用 XML 实体的实体读取器,并将它们存储在 Source 对象中。
例 51.2. XML 源实体读取器
import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Provider; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; import org.apache.cxf.jaxrs.ext.xml.XMLSource; @Provider @Consumes({"application/xml", "application/*+xml", "text/xml", "text/html" }) public class SourceProvider implements MessageBodyReader<Object> { public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) { return Source.class.isAssignableFrom(type) || XMLSource.class.isAssignableFrom(type); } public Object readFrom(Class<Object> source, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream is) throws IOException { if (DOMSource.class.isAssignableFrom(source)) { Document doc = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); doc = builder.parse(is); } catch (Exception e) { IOException ioex = new IOException("Problem creating a Source object"); ioex.setStackTrace(e.getStackTrace()); throw ioex; } return new DOMSource(doc); } else if (StreamSource.class.isAssignableFrom(source) || Source.class.isAssignableFrom(source)) { return new StreamSource(is); } else if (XMLSource.class.isAssignableFrom(source)) { return new XMLSource(is); } throw new IOException("Unrecognized source"); } }
自定义写入器
自定义实体作者负责将 Java 类型映射到 HTTP 实体。它们实施 javax.ws.rs.ext.MessageBodyWriter 接口。
例 51.3 “消息写入器接口” 中显示的接口有三个需要实现的方法:
例 51.3. 消息写入器接口
package javax.ws.rs.ext; public interface MessageBodyWriter<T> { public boolean isWriteable(java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType); public long getSize(T t, java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType); public void writeTo(T t, java.lang.Class<?> type, java.lang.reflect.Type genericType, java.lang.annotation.Annotation[] annotations, javax.ws.rs.core.MediaType mediaType, javax.ws.rs.core.MultivaluedMap<String, Object> httpHeaders, java.io.OutputStream entityStream) throws java.io.IOException, WebApplicationException; }
isWriteable()
isWriteable ()
方法确定实体写入器是否可以将 Java 类型映射到正确的实体类型。如果写入器可以进行映射,则方法会返回true
。表 51.4 “用于读取实体的参数” 描述
isWritable ()
方法的参数。表 51.4. 用于读取实体的参数 参数 类型 描述 type
类<T>
指定正在写入对象的 Java 类。
genericType
类型
指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。
GenericEntity
类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。annotations
Annotation[]
指定返回实体的方法上的注解列表。
mediaType
MediatType
指定 HTTP 实体的 MIME 类型。
getSize()
getSize ()
方法在writeTo ()
之前调用。它返回所写入实体的长度(以字节为单位)。如果返回正值,则该值将写入 HTTP 消息的Content-Length
标头中。表 51.5 “用于读取实体的参数” 描述
getSize ()
方法的参数。表 51.5. 用于读取实体的参数 参数 类型 描述 t
generic
指定正在写入的实例。
type
类<T>
指定正在写入对象的 Java 类。
genericType
类型
指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。
GenericEntity
类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。annotations
Annotation[]
指定返回实体的方法上的注解列表。
mediaType
MediatType
指定 HTTP 实体的 MIME 类型。
writeTo()
writeTo ()
方法将 Java 对象转换为所需的实体类型,并将实体写入输出流。如果在将实体写入输出流时发生错误,则方法应抛出 IOException 异常。如果发生错误需要 HTTP 错误响应,应抛出带有 HTTP 响应的 WebApplicationException。表 51.6 “用于读取实体的参数” 描述
writeTo ()
方法的参数。表 51.6. 用于读取实体的参数 参数 类型 描述 t
generic
指定正在写入的实例。
type
类<T>
指定正在写入对象的 Java 类。
genericType
类型
指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。
GenericEntity
类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。annotations
Annotation[]
指定返回实体的方法上的注解列表。
mediaType
MediatType
指定 HTTP 实体的 MIME 类型。
httpHeaders
MultivaluedMap<String, Object>
指定与实体关联的 HTTP 响应标头。
entityStream
OutputStream
指定将实体写入的输出流。
在 MessageBodyWriter 实现可用作实体写入器之前,它必须使用 javax.ws.rs.ext.Provider
注解进行分离。@Provider
注释提醒提供的实施提供了额外功能的运行时。这个实现也必须使用运行时注册,如 “注册阅读器和作者”一节 所述。
默认情况下,自定义实体供应商处理所有 MIME 类型。您可以使用 javax.ws.rs.Produces
注解限制自定义实体写器将处理的 MIME 类型。@Produces
注释指定自定义实体提供程序生成的以逗号分隔的 MIME 类型列表。如果实体不是指定的 MIME 类型,则不会选择实体提供程序作为可能的写器。
例 51.4 “XML 源实体编写器” 显示接受 Source 对象并生成 XML 实体的实体编写器。
例 51.4. XML 源实体编写器
import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.apache.cxf.jaxrs.ext.xml.XMLSource; @Provider @Produces({"application/xml", "application/*+xml", "text/xml" }) public class SourceProvider implements MessageBodyWriter<Source> { public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) { return Source.class.isAssignableFrom(type); } public void writeTo(Source source, Class<?> clazz, Type genericType, Annotation[] annotations, MediaType mediatype, MultivaluedMap<String, Object> httpHeaders, OutputStream os) throws IOException { StreamResult result = new StreamResult(os); TransformerFactory tf = TransformerFactory.newInstance(); try { Transformer t = tf.newTransformer(); t.transform(source, result); } catch (TransformerException te) { te.printStackTrace(); throw new WebApplicationException(te); } } public long getSize(Source source, Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) { return -1; } }
注册阅读器和作者
在 JAX-RS 应用可以使用任何自定义实体提供程序之前,必须在运行时注册自定义提供程序。提供程序使用应用配置文件中的 jaxrs:providers
元素或使用 JAXRSServerFactoryBean
类通过运行时注册。
jaxrs:providers
元素是 jaxrs:server
元素的子级,包含 bean
元素的列表。每个 bean
元素定义一个实体提供程序。
例 51.5 “使用运行时注册实体供应商” 显示 JAX-RS 服务器,配置为使用一组自定义实体提供程序。
例 51.5. 使用运行时注册实体供应商
<beans ...> <jaxrs:server id="customerService" address="/"> ... <jaxrs:providers> <bean id="isProvider" class="com.bar.providers.InputStreamProvider"/> <bean id="longProvider" class="com.bar.providers.LongProvider"/> </jaxrs:providers> </jaxrs:server> </beans>
JAXRSServerFactoryBean
类是 Apache CXF 扩展,提供对配置 API 的访问。它有一个 setProvider ()
方法,允许您将实例化实体供应商添加到应用程序中。例 51.6 “以编程方式注册实体供应商” 显示用于以编程方式注册实体提供程序的代码。
例 51.6. 以编程方式注册实体供应商
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; ... JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); ... SourceProvider provider = new SourceProvider(); sf.setProvider(provider); ...