第 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 ()方法的参数。Expand 表 51.2. 用于确定读者是否可以生成实体的参数 参数 类型 描述 typeclass<T>指定用于存储实体的对象的实际 Java 类。
genericType类型
指定用于存储实体的对象的 Java 类型。例如,如果消息正文被转换为 method 参数,则该值将是
Method.getGenericParameterTypes ()方法返回的 method 参数的类型。annotationsAnnotation[]
指定为存储实体创建的对象声明中的注解列表。例如,如果消息正文转换为方法参数,这将是该参数上由
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.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);
...