减少 SOAP 客户端中的耦合
Reduce coupling in a SOAP client
目前我们使用 jax-ws 来创建和使用 SOAP Web 服务,通过 wsimport 目标生成 java classes。
然后,我们创建一个服务 class 来调用此 Web 服务。服务class中的代码是这样的:
public WebServiceRS webServiceCall() {
Security security = SecurityFactory.getTokenSecurity();
MessageHeader header = MessageHeaderFactory.getMessageHeader(SERVICE_ACTION);
LOGGER.info("Calling CreatePassengerNameRecordRQ webService ...");
Holder<Security> securityHolder = new Holder<>(security);
Holder<MessageHeader> headerHolder = new Holder<>(header);
addHandlers(port);
WebServiceRS webServiceRS = port.webServiceRQ(
headerHolder,
securityHolder,
getRequestBody()
);
...
return webServiceRS
}
所以我们注意到并且可能有问题的是此服务 class 依赖于 .wsdl 生成的文件,如 Security、MessageHeader、WebServiceRS 等。因此,当我们将 .wsdl 文件更新到更新版本时,代码最终会因为这种依赖性而中断。
那么,我们正在努力实现的是一种反转这种依赖关系的方法,以便我们可以在不更改我们的服务的情况下更新 Web 服务(即生成 classes 的 .wsdl)class.我们遇到问题是因为我们还没有想出解决这个问题的模式。主要困难似乎是我们无法更改这些生成的 classe 以实现接口作为示例。
我们对可以松散这种耦合的模式或最佳实践很感兴趣。
您可以在代理后面设置 class。这样,您唯一需要适应 .wsdl 文件更改的就是代理 class.
解决方法一、使用Adapter pattern
在您的情况下,服务合同将发生变化,您需要同步两个不同的对象。适配器将为其主题提供不同的接口。
- 为您的客户端模型创建一个界面
public interface ClientModel {
String getValue();
}
- 创建将
DTO
转换为 ClientModel
的适配器
public class ClientModelAdapter implements ClientModel {
private DTO dto;
public ClientModelAdapter(DTO dto) {
this.dto = dto;
}
@Override
public String getValue() {
return dto.getValue();
}
}
public class DTO {
private String value;
public String getValue() {
return value;
}
}
解决方案2.使用映射器模式
您可以简单地在 DTO
和 ClientModel
之间创建一个映射器
public class ClientModelMapper {
ClientModel map(DTO dto) {
ClientModel clientModel = new ClientModel();
clientModel.setValue(dto.getValue());
return clientModel;
}
}
传输数据的映射器组件,确保 DTO 和客户端模型不需要相互了解。
在这种情况下,您可以使用MapStruct框架来避免手工映射。
解决方案 3. 使服务 API 向后兼容
也许您可以避免 SOAP 服务中的破坏性更改并使 API 向后兼容。 Backwards compatibility and Web Services
目前我们使用 jax-ws 来创建和使用 SOAP Web 服务,通过 wsimport 目标生成 java classes。
然后,我们创建一个服务 class 来调用此 Web 服务。服务class中的代码是这样的:
public WebServiceRS webServiceCall() {
Security security = SecurityFactory.getTokenSecurity();
MessageHeader header = MessageHeaderFactory.getMessageHeader(SERVICE_ACTION);
LOGGER.info("Calling CreatePassengerNameRecordRQ webService ...");
Holder<Security> securityHolder = new Holder<>(security);
Holder<MessageHeader> headerHolder = new Holder<>(header);
addHandlers(port);
WebServiceRS webServiceRS = port.webServiceRQ(
headerHolder,
securityHolder,
getRequestBody()
);
...
return webServiceRS
}
所以我们注意到并且可能有问题的是此服务 class 依赖于 .wsdl 生成的文件,如 Security、MessageHeader、WebServiceRS 等。因此,当我们将 .wsdl 文件更新到更新版本时,代码最终会因为这种依赖性而中断。
那么,我们正在努力实现的是一种反转这种依赖关系的方法,以便我们可以在不更改我们的服务的情况下更新 Web 服务(即生成 classes 的 .wsdl)class.我们遇到问题是因为我们还没有想出解决这个问题的模式。主要困难似乎是我们无法更改这些生成的 classe 以实现接口作为示例。
我们对可以松散这种耦合的模式或最佳实践很感兴趣。
您可以在代理后面设置 class。这样,您唯一需要适应 .wsdl 文件更改的就是代理 class.
解决方法一、使用Adapter pattern
在您的情况下,服务合同将发生变化,您需要同步两个不同的对象。适配器将为其主题提供不同的接口。
- 为您的客户端模型创建一个界面
public interface ClientModel {
String getValue();
}
- 创建将
DTO
转换为ClientModel
的适配器
public class ClientModelAdapter implements ClientModel {
private DTO dto;
public ClientModelAdapter(DTO dto) {
this.dto = dto;
}
@Override
public String getValue() {
return dto.getValue();
}
}
public class DTO {
private String value;
public String getValue() {
return value;
}
}
解决方案2.使用映射器模式
您可以简单地在 DTO
和 ClientModel
public class ClientModelMapper {
ClientModel map(DTO dto) {
ClientModel clientModel = new ClientModel();
clientModel.setValue(dto.getValue());
return clientModel;
}
}
传输数据的映射器组件,确保 DTO 和客户端模型不需要相互了解。
在这种情况下,您可以使用MapStruct框架来避免手工映射。
解决方案 3. 使服务 API 向后兼容
也许您可以避免 SOAP 服务中的破坏性更改并使 API 向后兼容。 Backwards compatibility and Web Services