DubboX的rest服务注册示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:protocol name="rest"
port="6666"
threads="500"
contextpath="wyl"
server="tomcat"
accepts="500"
extension="com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter"/>
<dubbo:service interface="com.wyl.sys.module.client.rest.IDemoRestService" protocol="rest" ref="demoRestService" document="demo" />
</xml>
接口定义:
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
@Path("demo")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8",
MediaType.TEXT_XML + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8" })
public interface IDemoRestService {
@POST
@Path("xml")
String xml(XmlDto dto);
}
Dto定义:
import lombok.Data;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
@Data
@XmlRootElement(name="wyl")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlDto implements Serializable {
@XmlElement(name="name")
private String name;
@XmlElement(name="address")
private String address;
}
请求参数示例:
<xml>
<name>test</name>
<address>chain</address>
</xml>
以上为使用javax的xml注解方式来实现XML协议的请求。
由于DubboX的Rest实现不支持使用XStream接收XML类型的入参,具体可查看:
org.jboss.resteasy.spi.ResteasyProviderFactory
各种协议的解析类映射关系可查看:
org.jboss.resteasy.core.MediaTypeMap
其内置支持的provider为:
org.jboss.resteasy.plugins.providers.jaxb.JAXBXmlSeeAlsoProvider
org.jboss.resteasy.plugins.providers.jaxb.JAXBXmlRootElementProvider
org.jboss.resteasy.plugins.providers.jaxb.JAXBXmlTypeProvider
org.jboss.resteasy.plugins.providers.jaxb.CollectionProvider
org.jboss.resteasy.plugins.providers.jaxb.MapProvider
com.alibaba.fastjson.support.jaxrs.FastJsonProvider
org.jboss.resteasy.plugins.providers.DocumentProvider
org.jboss.resteasy.plugins.providers.SourceProvider
org.jboss.resteasy.plugins.providers.jaxb.JAXBElementProvider
org.jboss.resteasy.plugins.providers.DataSourceProvider
org.jboss.resteasy.plugins.providers.StringTextStar
org.jboss.resteasy.plugins.providers.InputStreamProvider
org.jboss.resteasy.plugins.providers.ReaderProvider
org.jboss.resteasy.plugins.providers.ByteArrayProvider
org.jboss.resteasy.plugins.providers.FileProvider
如果要使用XStream,则需要自行对dubbo协议进行扩展。
首先增加XStream解析工具类:
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.basic.*;
import com.thoughtworks.xstream.converters.collections.CollectionConverter;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.thoughtworks.xstream.security.NoTypePermission;
import com.thoughtworks.xstream.security.WildcardTypePermission;
import java.io.Writer;
public class XStreamInitializer {
public static ClassLoader classLoader;
public static void setClassLoader(ClassLoader classLoaderInfo) {
classLoader = classLoaderInfo;
}
private static final XppDriver XPP_DRIVER = new XppDriver() {
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out, getNameCoder()) {
private static final String PREFIX_CDATA = "<![CDATA[";
private static final String SUFFIX_CDATA = "]]>";
private static final String PREFIX_MEDIA_ID = "<MediaId>";
private static final String SUFFIX_MEDIA_ID = "</MediaId>";
private static final String PREFIX_REPLACE_NAME = "<ReplaceName>";
private static final String SUFFIX_REPLACE_NAME = "</ReplaceName>";
@Override
protected void writeText(QuickWriter writer, String text) {
if (text.startsWith(PREFIX_CDATA) && text.endsWith(SUFFIX_CDATA)) {
writer.write(text);
} else if (text.startsWith(PREFIX_MEDIA_ID) && text.endsWith(SUFFIX_MEDIA_ID)) {
writer.write(text);
} else if (text.startsWith(PREFIX_REPLACE_NAME) && text.endsWith(SUFFIX_REPLACE_NAME)) {
writer.write(text);
} else {
super.writeText(writer, text);
}
}
@Override
public String encodeNode(String name) {
//防止将_转换成__
return name;
}
};
}
};
/**
* Gets instance.
*
* @return the instance
*/
public static XStream getInstance() {
XStream xstream = new XStream(new PureJavaReflectionProvider(), XPP_DRIVER) {
// only register the converters we need; other converters generate a private access warning in the console on Java9+...
@Override
protected void setupConverters() {
registerConverter(new NullConverter(), PRIORITY_VERY_HIGH);
registerConverter(new IntConverter(), PRIORITY_NORMAL);
registerConverter(new FloatConverter(), PRIORITY_NORMAL);
registerConverter(new DoubleConverter(), PRIORITY_NORMAL);
registerConverter(new LongConverter(), PRIORITY_NORMAL);
registerConverter(new ShortConverter(), PRIORITY_NORMAL);
registerConverter(new BooleanConverter(), PRIORITY_NORMAL);
registerConverter(new ByteConverter(), PRIORITY_NORMAL);
registerConverter(new StringConverter(), PRIORITY_NORMAL);
registerConverter(new DateConverter(), PRIORITY_NORMAL);
registerConverter(new CollectionConverter(getMapper()), PRIORITY_NORMAL);
registerConverter(new ReflectionConverter(getMapper(), getReflectionProvider()), PRIORITY_VERY_LOW);
}
};
xstream.ignoreUnknownElements();
xstream.setMode(XStream.NO_REFERENCES);
xstream.autodetectAnnotations(true);
xstream.addPermission(NoTypePermission.NONE);
xstream.addPermission(new WildcardTypePermission(new String[]{"com.**"}));
if (null == classLoader) {
classLoader = Thread.currentThread().getContextClassLoader();
}
xstream.setClassLoader(classLoader);
return xstream;
}
}
import com.thoughtworks.xstream.XStream;
import java.util.*;
public class XmlTransformer {
private static final Map<Class<?>, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>();
static {
registerClass(XmlDto.class);
}
/**
* XML转POJO
*/
@SuppressWarnings("unchecked")
public static <T> T xmlToPojo(String xml, Class<T> clazz) {
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml);
return object;
}
/**
* POJO转XML
*/
public static <T> String pojoToXml(T object, Class<T> clazz) {
return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object);
}
private static void registerClass(Class<?> clz) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(clz);
xstream.processAnnotations(getInnerClasses(clz));
register(clz, xstream);
}
private static Class<?>[] getInnerClasses(Class<?> clz) {
Class<?>[] innerClasses = clz.getClasses();
if (innerClasses == null) {
return null;
}
List<Class<?>> result = new ArrayList<>();
result.addAll(Arrays.asList(innerClasses));
for (Class<?> inner : innerClasses) {
Class<?>[] innerClz = getInnerClasses(inner);
if (innerClz == null) {
continue;
}
result.addAll(Arrays.asList(innerClz));
}
return result.toArray(new Class<?>[0]);
}
public static void register(Class<?> clz, XStream xStream) {
CLASS_2_XSTREAM_INSTANCE.put(clz, xStream);
}
/**
* 注册第三方的该类及其子类.
* 便利第三方类使用 XStreamTransformer进行序列化, 以及支持XStream 1.4.18 以上增加安全许可
*
* @param clz 要注册的类
*/
public static void registerExtendClass(Class<?> clz) {
XStream xstream = XStreamInitializer.getInstance();
Class<?>[] innerClz = getInnerClasses(clz);
xstream.processAnnotations(clz);
xstream.processAnnotations(innerClz);
xstream.allowTypes(new Class[]{clz});
xstream.allowTypes(innerClz);
register(clz, xstream);
}
}
新增Dubbo provider(仅为示例)
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.jboss.resteasy.plugins.providers.AbstractEntityProvider;
import javax.ws.rs.Consumes;
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.Provider;
import javax.xml.bind.annotation.XmlElement;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
@Provider
@Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
@Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
public class XStreamProvider extends AbstractEntityProvider {
@Override
public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type.isAnnotationPresent(XStreamAlias.class);
}
@Override
public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
StringBuilder sb = new StringBuilder();
try(BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream))) {
String line;
while((line = reader.readLine()) != null) {
sb.append(line);
}
}
return XmlTransformer.xmlToPojo(sb.toString(), type);
}
@Override
public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return false;
}
@Override
public void writeTo(Object o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
}
}
修改DTO上的注解,使用XStream的注解:
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
@Data
@XStreamAlias("xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlDto implements Serializable {
@XmlElement(name="name")
@XStreamAlias("name")
private String name;
@XmlElement(name="address")
@XStreamAlias("address")
private String address;
}
最后在dubbo的REST服务注册中,添加该扩展:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:protocol name="rest"
port="7777"
threads="500"
contextpath="eoswpc"
server="tomcat"
accepts="500"
extension="com.wyl.service.base.provider.XStreamProvider"/>
</xml>