SI in Action:HTTP + Spring ws

HTTP

SI in Action:HTTP + Spring ws_第1张图片
declarative就是解耦

  我们知道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:. 之后的处理逻辑可以是同样写在xml配置文件里的表达式而不需要代码,和各种DSL结合的轻量xml处理也算是si的目标吧。

SI in Action:HTTP + Spring ws_第2张图片
SI架构

  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. 

SI in Action:HTTP + Spring ws_第3张图片
云总线必须直接跨平台,云更需要可以良好跨平台的交互协议
SI in Action:HTTP + Spring ws_第4张图片
AMQP可以代替WebService的地位......
SI in Action:HTTP + Spring ws_第5张图片
AMQP位置,要跨平台,transport必须是开放式系统互连协议 (Open System Interconnect ,OSI),可以是TCP或Http;schema随意可以是私有的、只要是开放开源的、并不是必须得xml/json

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头部,不过没什么影响;

SI in Action:HTTP + Spring ws_第6张图片
Hey Ho

  至于Spring ws,需要注意的是它只支持“从上到下”的ws开发,也即Contract First. 这很务实,看看ws的历史明白一下为什么soap坑爹:

  由于TCP是无处不在的,而HTTP是TCP之上考虑相对来说最全面的、应用最广的“应用层”协议,所以用HTTP进行跨系统的ws交互就成了最通用的一种消息交互形式:

SI in Action:HTTP + Spring ws_第7张图片
ws底层协议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都是来革命的。

SI in Action:HTTP + Spring ws_第8张图片
EJB、SOA,多少曾经的权威一地鸡毛

  起初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"/>

//输出通道、输出payLoad为Result的消息。对javax.xml.transform.Result能做的只是get/set system ID,用于错误消息或者relate to a file on the local filesystem.

POX

  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;

}

SI in Action:HTTP + Spring ws_第9张图片
spring OXM作为spring WS项目的一部分开始开发、到spring3.0并入了核心spring framework.

  可以结合使用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;

  }

}

你可能感兴趣的:(SI in Action:HTTP + Spring ws)