HTTP
我们知道Spring框架可以帮助开发者实现面向接口编程,基于dependency injection (DI)可以为POJO注入依赖,这样起码实现了编译时解耦(运行时依然是实际对象之间紧耦合);Spring Integration更进一步:SI中POJOs可以作为处理节点,节点之间使用消息通道Channel串接起来形成完整的消息处理管道Pipe,由于Channel是通用组件,因此节点之间完全无耦合(开发编译时和运行时都没有耦合)。这样的应用程序是通过通道组装各种细粒度、可重用的节点构成功能管道(Such an application is built by assembling fine-grained reusable components to form a higher level of functionality.),每一条组装好的管道甚至还可以再次组合,si与springBatch及spring ws等可以结合。
除了内部的这些消息通道,对外SI提供有通道适配器(channel adapters) 和网关(gateways) 来导入外部系统的消息:
1、通道适配器是单向的one-way、只能发或者收;
2、gateway是r-r双向request/reply、可以同时做应答;
比如inbound-channel-adapter只接收不响应可用于文件、inbound-gateway可以响应。 Spring Cloud Stream项目使用SI作为消息驱动的微服务运行引擎。官网示例Examples展示一个gateway接口(而且只有接口)可用于两种不同的服务实现:一个是最简单的请求响应一个是webservice,这需要spring-integration-ws 和spring-integration-xml模块。Spring3位spring-ws扩展了restful支持,si2.0使用了Spring3的REST支持,用spring-ws入栈网关暴露POX/soap接口。
SI的gateway可以作为抽象的调用stub:
si里有意义的一个东西是Http,因为借助它我们可以绕过SOAP,SOAP坑爹很久了、它的目标:轻量、简单全盘失败,SOAP和它采用的XML一样低效和死板。但对于遗留的soap格式交互无法采用rest的,就可以考虑基于Http直接处理soap xml、直接手撸(un)mashall,si起了个术语叫做:POX(plain old xml)、这样可以抛开WS-*、避开SOAP的复杂性,解救码农于水火。
Protocol交互协议分为schema/encoding(消息格式如json、xml)和transport(传输协议如TCP、Http)两方面:
1、rest:transport是Http;schema随意一般用json,json属于schemaless半格式化数据、json不须要类似xml的xsd这种schema,在简单场景下,可以不做序列化直接用官方参考实现JSON-java处理字符串KV映射;由于json这种schemaless的原因、调用服务之间算是松耦合。
2、SOAP:transport随意一般是Http、只要是跨平台协议都可以如SMTP、MIME;schema固定是xml(WSDL/xsd);调用服务之间是紧耦合死的。
3、gRPC:transport是Http/2;schema固定是Protobuf —— 可以替代rest、json的序列化效率低下;与之类似,AMQP是vender neutral的、目标是天然跨平台:AMQP is the Internet Protocol for Business Messaging.
4、POX:transport是Http;schema是xml —— 替代SOAP;
gRPC可以部分代替rest、HTTP2+proto代替HTTP1.1+json,单纯的Http协议性能并不比TCP差太多,主要是文本的序列化性能损耗;SOAP自作孽不可活。要跨平台就要求Protocol交互协议整体是open Internet Protocol开放互联协议,包括:
1、transport是开放的传输协议(TCP是一种ad-hoc的开放协议,Http当然是最为开放的协议)
2、schema消息格式只要求开放即可,可以是专有实现只要公开且为各个异构平台提供SDK,如果包含schema实现,那么就称此种协议为wire Protocol线级协议,如AMQP、gRPC;SOAP不伦不类,虽然消息格式要求xml,但实际上只要有个xml的皮,里面任意文本都可以写,诸多因素造成SOAP ws混乱,实际当中用的是五花八门。
AMQP与ws的关系
ws-support:SI与Spring ws联合使用
如果接收到的是soap消息,那么要令Message headers 包含SOAP Action header. 就得使用 1、POX- and SOAP-based gateways 2、Simple HTTP-based integration;经测普通http入栈会抛弃soap消息的SOAP Action头部,不过没什么影响; 至于Spring ws,需要注意的是它只支持“从上到下”的ws开发,也即Contract First. 这很务实,看看ws的历史明白一下为什么soap坑爹: 由于TCP是无处不在的,而HTTP是TCP之上考虑相对来说最全面的、应用最广的“应用层”协议,所以用HTTP进行跨系统的ws交互就成了最通用的一种消息交互形式: 但是实际上并没有对ws的“完全限定”定义,虽然W3C组织定义了soap和wsdl,但是在定义中也有免(甩)责(锅)声明,大意是我们只是定义个意思、工业界具体怎么用我们不管、对于其他定义我们也不置可否...我去尼玛soap可以看做只是个参考实现而已,你要是把它当作圣人之言那是你自己图样图森破。在REST和soap之间的争论也表明soap和wsdl绝非ws的唯一选择,而且即使是soap这一边,soap也不是只能以HTTP作为传输协议的。也就是说ws和soap并没有必然联系,作为通用跨系统交互ws形式确实是首选,至于消息格式那你可以自由决定,和ws没关系,soap可以不是ws/HTTP形式的、ws的消息格式也可以不是soap的。oasis的陈述是这样的:A Web Service is a component that is described via WSDL and is capable of being accessed via standard software network protocols such as but not limited to SOAP over HTTP. 但是即使说wsdl自己,它也绝不是一个通用的东西。所以出现了gRPC,依然使用跨平台的Http但是抛弃了xml,固定用proto. 对标准化ws的尝试在1990年前后十分盛行,大多数大型软件商乐于提供一揽子的工具赚钱、还有在WS-*规范里加上自己的一笔,使得WS-*规范几乎无所不包,这种情况可称之为:add-ons 重在掺和...这是囚徒困境,导致soap ws臃肿不堪,复杂且无用,使得很多人开始寻找可替代的、轻量级方案,其中之一就是REST。综上所述:SOAP迅速演变成了一场商业闹剧,随后REST、POX、gRPC都是来革命的。 起初ws被认为是一种RPC,之后人们发现ws和RPC的很多差异,ws更像消息,而xml则是数据的平台中立表达,是SOA构想的“世界语”。 实际上传输协议和消息格式本来就是无耦合的,基于Spring WS的SI继续遵循了这个最佳实践,其实在SI之前已经有了基于Spring WS和JMS协议、邮件协议以及XMPP(Extensible Messaging and Presence Protocol)的ws. SI首推POX,soap也支持。SI的纯HTTP的通道适配器一般是用于非xml的或者是REST的消息。无论是POX还是soap,对xml消息的处理都少不了,这方面建议用Spring OXM module. OXM模块起初是作为Spring WS子项目的一部分而开发,到spring3.0,OXM成为了spring core框架的一个模块,主要的俩接口: package org.springframework.oxm; public interface Marshaller { boolean supports(Class> clazz); void marshal(Object graph, Result result) throws IOException, XmlMappingException; } public interface Unmarshaller { boolean supports(Class> clazz); Object unmarshal(Source source) throws IOException, XmlMappingException; } 如果你的场景仅仅是想做JavaBean和XML节点元素的互相转换,而不涉及动态XML的处理——说人话就是直接手撸(un)mashall,那么JAXB绝对是一个不错的选择。在比较新的jdk版本中,JAXB都是jdk的扩展包javax中自带的类库,不需要你引入第三方jar包。如果是已经有了wsdl+xsd的从上到下方式,可以用JDK的对象生成工具: jdk1.8.0_151\bin>xjc -d C:\...\javaO -p ch.iec -readOnly -wsdl C:\...\*.wsdl -encoding UTF-8 从wsdl生成代码包ch.iec到javaO目录下。JAXB可以与OXM结合使用,其他场景比如是先有代码的从下至上方式,可以对业务对象类加JAXB注释,注意从下至上方式是不好的。@XmlJavaTypeAdapter可以做一些非JDK标准类型到标准类型的转换: @XmlElement @XmlJavaTypeAdapter(JodaDateTimeAdapter.class) private DateTime startOfLegDate; javax.xml.bind.annotation.adapters.XmlAdapter实现: public class JodaDateTimeAdapter extends XmlAdapter @Override public DateTime unmarshal(Calendar cal) throws Exception { return new DateTime(cal.getTime(), ISOChronology.getInstanceUTC()); } @Override public Calendar marshal(DateTime dt) throws Exception { GregorianCalendar cal = new GregorianCalendar(); cal.setTimeInMillis(dt.getMillis()); cal.setTimeZone(TimeZone.getTimeZone("UTC")); return cal; } } SI基于OXM做了一个很薄的适配器层封装,开箱即用,配置一个Jaxb2Marshaller: 配置中使用引入si-xml namespace: http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd 在两个通道之间做转换: result-transformer="resultToDocumentTransformer"/> POX也可以称之为XML web service with Spring ws. 从Spring WS开始就提倡并支持:可以解耦应用代码和复杂的soap处理,因为soap里头的xm和命名空间l处理很烦人。Spring WS已经做了编程模型上的简化:把spring配置与简单的基于接口/注释的端点相结合,SI继承了这种简单模型,并添加了简化的消息模型、做了EIP的实现。 按照EIP实现ws,可以做诸如对消息的split和聚合,你可以只用Spring WS做到,但再加上SI可以省略不少工作,这就是为什么SI要基于Spring WS提供ws支持。要对外暴露纯正的ws服务,可以用Spring WS的入栈网关,支持POX及soap,配合使用SI的int-ws:inbound-gateway可以将ws请求导入SI消息通道。也可以只用纯HTTP组件,对soap自己做marshal。用Spring WS得配置MessageDispatcherServlet(也可以不配的,它可以将ws请求引入si消息管道),它带有getWsdlDefinition、getXsdSchema方法。该servlet专用于简化ws消息的分发,是对springMVC DispatcherServlet的替代。在Spring-WS中可以基于xsd和一些转换动态生成wsdl。也可以不使用DispatcherServlet,直接配置UriEndpointMapping,不过url path都固定为services. 集成处理一般不需要理解消息内容,那是所集成的业务系统的事,但有时需要对内容做特定处理比如基于内容的路由、消息内容增强之类,这类简单处理可以用XPath或者Extensible Stylesheet Language Transformation (XSLT). 如果是更复杂的处理须要将xml转换为业务对象的,可以用object-to-XML mapping (OXM) 做marshalling and unmarshalling. spring OXM做了一层技术无关的java与xml映射的通用抽象、通过这个通用抽象层将代码与实现细节解耦,并提供了一致的异常处理层,这种解耦是通过封装Marshaller的对象到xml映射以及Unmarshaller映射实现的: package org.springframework.oxm; public interface Marshaller { boolean supports(Class> clazz); void marshal(Object graph, Result result) throws IOException, XmlMappingException; } package org.springframework.oxm; public interface Unmarshaller { boolean supports(Class> clazz); Object unmarshal(Source source) throws IOException, XmlMappingException; } 可以结合使用Java Architecture for XML Binding (JAXB) v2 与 spring OXM. 你可以使用JAXB注释去注释业务类而不是从xsd生成业务类,比如使用javax.xml.bind.annotation.XmlRootElement注释声明文档根,像这样: import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "legQuote") public class LegQuoteCommand implements Command { private Leg leg; private HotelCriteria hotelCriteria; private FlightCriteria flightCriteria; ....... 还可以使用XmlJavaTypeAdapter注释去适配一些非JDK类: @XmlRootElement public class Leg { @XmlElement @XmlJavaTypeAdapter(JodaDateTimeAdapter.class) private DateTime startOfLegDate; ..... JodaDateTimeAdapter 是这样的: public class JodaDateTimeAdapter extends XmlAdapter @Override public DateTime unmarshal(Calendar cal) throws Exception { return new DateTime(cal.getTime(), ISOChronology.getInstanceUTC()); } @Override public Calendar marshal(DateTime dt) throws Exception { GregorianCalendar cal = new GregorianCalendar(); cal.setTimeInMillis(dt.getMillis()); cal.setTimeZone(TimeZone.getTimeZone("UTC")); return cal; } }POX