第 51 章 实体支持
摘要
Apache CXF 运行时支持 MIME 类型和 Java 对象之间的有限数量映射。开发人员可以通过实施自定义读取器和写入器来扩展映射。自定义读取器和写入器会在启动时通过运行时注册。
概述
该运行时依赖于 JAX-RS MessageBodyReader 和 MessageBodyWriter 实施在 HTTP 消息和其 Java 消息之间对数据进行序列化和反序列化。readers 和 writers 可以限制它们能够处理的 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
class<T>
指定用于存储实体的对象的实际 Java 类。
genericType
类型
指定用于存储实体的对象的 Java 类型。例如,如果消息正文被转换为 method 参数,则该值将是
Method.getGenericParameterTypes ()
方法返回的 method 参数的类型。annotations
Annotation[]
指定为存储实体创建的对象声明中的注解列表。例如,如果消息正文转换为方法参数,这将是该参数上由
Method.getParameterAnnotations ()
方法返回的注解。mediaType
MediatType
指定 HTTP 实体的 MIME 类型。
readFrom()
readFrom ()
方法读取 HTTP 实体,并将其覆盖到所需的 Java 对象中。如果读取成功,方法会返回包含该实体创建的 Java 对象。如果在读取输入流时出现错误,则方法应抛出 IOException 异常。如果发生错误需要 HTTP 错误响应,则应抛出带有 HTTP 响应的 WebApplicationException。表 51.3 “用于读取实体的参数” 描述
readFrom ()
方法的参数。表 51.3. 用于读取实体的参数 参数 类型 描述 type
class<T>
指定用于存储实体的对象的实际 Java 类。
genericType
类型
指定用于存储实体的对象的 Java 类型。例如,如果消息正文被转换为 method 参数,则该值将是
Method.getGenericParameterTypes ()
方法返回的 method 参数的类型。annotations
Annotation[]
指定为存储实体创建的对象声明中的注解列表。例如,如果消息正文转换为方法参数,这将是该参数上由
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
class<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
class<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
class<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); ...