我有基于
SpringBoot
、SpringData JPA
、Axon
的沙箱应用程序微服务。
我创建了 2 个简单的微服务:订单服务和产品服务,并尝试探索 Axon Sagas。在 Saga 交易期间,我执行订单创建命令,当它发生时,Saga 发出产品储备事件。此事件在产品服务中处理,但失败并显示:
Exception in thread "CommandProcessor-0" com.thoughtworks.xstream.security.ForbiddenClassException: com.udemy.shared.command.ReserveProductCommand
如何解决?
订单微服务中的控制器代码:
@PostMapping
public String createOrder(@Valid @RequestBody OrderDTO order) {
CreateOrderCommand createOrderCommand = CreateOrderCommand.builder()
.orderId(UUID.randomUUID().toString())
.userId("27b95829-4f3f-4ddf-8983-151ba010e35b")
.productId(order.getProductId())
.quantity(order.getQuantity())
.addressId(order.getAddressId())
.orderStatus(OrderStatus.CREATED)
.build();
return commandGateway.sendAndWait(createOrderCommand);
}
命令代码:
@Builder
@Data
public class CreateOrderCommand {
@AggregateIdentifier
private final String orderId;
private final String userId;
private final String productId;
private final int quantity;
private String addressId;
private final OrderStatus orderStatus;
}
@Data
@Builder
public class ReserveProductCommand {
@AggregateIdentifier
private String productId;
private String orderId;
private String userId;
private int quantity;
}
传奇代码:
@Slf4j
@Saga
public class OrdersSaga {
@Autowired
private transient CommandGateway commandGateway;
@StartSaga
@SagaEventHandler(associationProperty = "orderId")
public void handle(OrderCreatedEvent event) {
ReserveProductCommand reserveProductCommand = ReserveProductCommand.builder()
.orderId(event.getOrderId())
.productId(event.getProductId())
.userId(event.getUserId())
.quantity(event.getQuantity())
.build();
commandGateway.send(reserveProductCommand, (commandMessage, commandResultMessage) -> {
if (commandResultMessage.isExceptional()) {
log.error("Something went wrong during product reserve: " + commandResultMessage.exceptionResult().getMessage() );
}
});
log.info("Created order command fired! Order id = " + event.getOrderId());
}
@SagaEventHandler(associationProperty = "orderId")
public void handle(ProductReservedEvent event) {
log.info("Handling product reserve event for product with id = " + event.getProductId());
}
}
发生错误的处理程序(产品微服务):
@Slf4j
@Component
public class ProductEventHandler {
ProductsRepository repository;
@EventHandler
public void on(ProductReservedEvent event) {
ProductEntity updatedProduct = repository.findByProductId(event.getProductId());
updatedProduct.setQuantity(event.getQuantity());
log.info("Product reserved event was applied in event handler for product with id - " + event.getProductId());
repository.save(updatedProduct);
}
}
谷歌搜索后我发现,不同的
Spring Boot
版本可以有不同的xstream
版本,更相关的xstream会产生这个异常。我将这些服务中的 Spring Boot 版本降级为2.7.8
,但这没有帮助
我的项目的 JDK 和结构可以在屏幕上看到
Sam,我猜你使用的是 JDK17 或更高版本。就目前而言,XStream 不能很好地与较新的 JDK 版本一起使用,因为它非常依赖于反射。由于正在关闭,因此可能会出现异常。
可悲的是,Axon Framework 默认使用所谓的
XStreamSerializer
。将其更改为其他内容会导致所有 Framework 用户发生重大更改。因此,默认卡住了。
为了在 Spring Boot 环境中解决这个问题,框架为您连接了一个自定义
XStream
实例,将您的 @SpringBootApplication
注释类的包名称添加到 XStream 的安全上下文中。不过,Axon 确实记录了一条关于此的 WARN 消息,因为强烈建议您根据要反序列化/反序列化的对象自己定义 XStream 安全上下文。
无论如何,通过这条路线,10 个场景中有 9 个都被反序列化覆盖。但是,我假设您的
ReserveProductCommand
位于不同的包中。
好吧,即使不是,也建议您自己定义 XStream 的安全上下文。 或者,您可以通过定义
Serializer
将 Axon Framework 的 JacksonSerializer
从 XML 切换到 JSON。
特别是对于您的消息(命令、事件和查询),建议使用 JSON 的较小格式。这样可以节省网络流量和存储空间。
您可能会注意到,我对您的 JDK 版本和包结构做了一些假设。如果我建议的解决方案没有解决困境,请务必发表评论。