第 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 ()方法的参数。Expand 表 51.2. 用于确定读取器是否可以生成实体的参数 参数 类型 描述 typeClass<T>指定用于存储实体的对象的实际 Java 类。
genericType类型
指定用于存储实体的对象的 Java 类型。例如,如果要将消息正文转换为 method 参数,则该值将是
Method.getGenericParameterTypes ()方法返回的 method 参数的类型。annotationsannotation[]
指定为存储实体而创建的对象声明的注解列表。例如,如果消息正文要转换为 method 参数,这将是
Method.getParameterAnnotations ()方法返回的该参数上的注解。mediaTypeMediatType指定 HTTP 实体的 MIME 类型。
readFrom()readFrom ()方法读取 HTTP 实体,并将其覆盖到所需的 Java 对象中。如果读取成功,则方法返回包含该实体的创建的 Java 对象。如果在读取输入流时发生错误,方法应抛出 IOException 异常。如果发生错误需要 HTTP 错误响应,应抛出带有 HTTP 响应的 WebApplicationException。表 51.3 “用于读取实体的参数” 描述
readFrom ()方法的参数。Expand 表 51.3. 用于读取实体的参数 参数 类型 描述 typeClass<T>指定用于存储实体的对象的实际 Java 类。
genericType类型
指定用于存储实体的对象的 Java 类型。例如,如果要将消息正文转换为 method 参数,则该值将是
Method.getGenericParameterTypes ()方法返回的 method 参数的类型。annotationsannotation[]
指定为存储实体而创建的对象声明的注解列表。例如,如果消息正文要转换为 method 参数,这将是
Method.getParameterAnnotations ()方法返回的该参数上的注解。mediaTypeMediatType指定 HTTP 实体的 MIME 类型。
httpHeadersMultivaluedMap<String, String>
指定与实体关联的 HTTP 消息标头。
entityStreamInputStream指定包含 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 ()方法的参数。Expand 表 51.4. 用于读取实体的参数 参数 类型 描述 typeClass<T>指定正在写入对象的 Java 类。
genericType类型
指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。
GenericEntity类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。annotationsannotation[]
指定返回实体的方法上的注解列表。
mediaTypeMediatType指定 HTTP 实体的 MIME 类型。
getSize()getSize ()方法在writeTo ()之前调用。它返回所写入实体的长度(以字节为单位)。如果返回正值,则该值将写入 HTTP 消息的Content-Length标头中。表 51.5 “用于读取实体的参数” 描述
getSize ()方法的参数。Expand 表 51.5. 用于读取实体的参数 参数 类型 描述 tgeneric
指定正在写入的实例。
typeClass<T>指定正在写入对象的 Java 类。
genericType类型
指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。
GenericEntity类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。annotationsannotation[]
指定返回实体的方法上的注解列表。
mediaTypeMediatType指定 HTTP 实体的 MIME 类型。
writeTo()writeTo ()方法将 Java 对象转换为所需的实体类型,并将实体写入输出流。如果在将实体写入输出流时发生错误,则方法应抛出 IOException 异常。如果发生错误需要 HTTP 错误响应,应抛出带有 HTTP 响应的 WebApplicationException。表 51.6 “用于读取实体的参数” 描述
writeTo ()方法的参数。Expand 表 51.6. 用于读取实体的参数 参数 类型 描述 tgeneric
指定正在写入的实例。
typeClass<T>指定正在写入对象的 Java 类。
genericType类型
指定要编写的对象的 Java 类型,通过反映资源方法返回类型或通过检查返回的实例来获取。
GenericEntity类(如 第 48.4 节 “使用通用类型信息返回实体” 所述)提供对控制这个值的支持。annotationsannotation[]
指定返回实体的方法上的注解列表。
mediaTypeMediatType指定 HTTP 实体的 MIME 类型。
httpHeadersMultivaluedMap<String, Object>
指定与实体关联的 HTTP 响应标头。
entityStreamOutputStream指定将实体写入的输出流。
在 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);
...