设计模式框架之登高望远

使用成熟的框架来实现使用模式框架,只需要在框架下编写业务内容

一、责任链模式

将不同职责的步骤串联起来执行,并且一个步骤执行完成之后才能够执行下一个步骤。

通常责任链模式使用链表来完成。

当执行任务的请求发起时,从责任链上第一步开始往下传递,直到最后一个步骤完成。

在责任链模式当中,客户端只用执行一次流程开始的请求便不再需要参与到流程执行当中,责任链上的流程便能够自己一直往下执行,客户端同样也并不关心执行流程细节,从而实现与流程之间的解耦。

责任链框架

pie 源码地址:https://github.com/feiniaojin/pie.git

pie 案例工程源码地址:https://github.com/feiniaojin/pie-example.git

将netty中责任链框架抽出来实现

2. 快速入门

2.1 引入 maven 依赖

pie 目前已打包发布到 maven 中央仓库,开发者可以直接通过 maven 坐标将其引入到项目中。

<dependency>
    <groupId>com.feiniaojin.ddd.ecosystem</groupId>
    <artifactId>pie</artifactId>
    <version>1.0</version>
</dependency>

目前最新的版本是 1.0

2.2 实现出参工厂

出参也就是执行结果,一般的执行过程都要求有执行结果返回。实现 OutboundFactory 接口,用于产生接口默认返回值。

例如:

public class OutFactoryImpl implements OutboundFactory {
    @Override
    public Object newInstance() {
        Result result = new Result();
        result.setCode(0);
        result.setMsg("ok");
        return result;
    }
}
2.3 实现 handler 接口完成业务逻辑

在 pie 案例工程( https://github.com/feiniaojin/pie-example.git )的 Example1 中,为了展示 pie 的使用方法,实现了一个虚拟的业务逻辑:CMS类项目修改文章标题、正文,大家不要关注修改操作放到两个 handler 中是否合理,仅作为讲解案例。

三个 Handler 功能如下:

CheckParameterHandler:用于参数校验。

ArticleModifyTitleHandler:用于修改文章的标题。

ArticleModifyContentHandler:用于修改文章的正文。

CheckParameterHandler 的代码如下:

public class CheckParameterHandler implements ChannelHandler {
    private Logger logger = LoggerFactory.getLogger(CheckParameterHandler.class);

    @Override
    public void channelProcess(ChannelHandlerContext ctx, Object in, Object out) throws Exception {
        logger.info("参数校验:开始执行");
        if (in instanceof ArticleTitleModifyCmd) {
            ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in;
            String articleId = cmd.getArticleId();
            Objects.requireNonNull(articleId, "articleId不能为空");
            String title = cmd.getTitle();
            Objects.requireNonNull(title, "title不能为空");
            String content = cmd.getContent();
            Objects.requireNonNull(content, "content不能为空");
        }
        logger.info("参数校验:校验通过,即将进入下一个Handler");
        ctx.fireChannelProcess(in, out);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception {
        logger.error("参数校验:异常处理逻辑", cause);
        Result re = (Result) out;
        re.setCode(400);
        re.setMsg("参数异常");
    }
}

ArticleModifyTitleHandler 的代码如下:

public class ArticleModifyTitleHandler implements ChannelHandler {
    private Logger logger = LoggerFactory.getLogger(ArticleModifyTitleHandler.class);

    @Override
    public void channelProcess(ChannelHandlerContext ctx, Object in, Object out) throws Exception {
        logger.info("修改标题:进入修改标题的Handler");
        ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in;
        String title = cmd.getTitle();        //修改标题的业务逻辑        
        logger.info("修改标题:title={}", title);
        logger.info("修改标题:执行完成,即将进入下一个Handler");
        ctx.fireChannelProcess(in, out);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception {
        logger.error("修改标题:异常处理逻辑");
        Result re = (Result) out;
        re.setCode(1501);
        re.setMsg("修改标题发生异常");
    }
}

ArticleModifyContentHandler 的代码如下:

public class ArticleModifyContentHandler implements ChannelHandler {
    private Logger logger = LoggerFactory.getLogger(ArticleModifyContentHandler.class);

    @Override
    public void channelProcess(ChannelHandlerContext ctx, Object in, Object out) throws Exception {
        logger.info("修改正文:进入修改正文的Handler");
        ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in;
        logger.info("修改正文,content={}", cmd.getContent());
        logger.info("修改正文:执行完成,即将进入下一个Handler");
        ctx.fireChannelProcess(in, out);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception {
        logger.error("修改标题:异常处理逻辑");
        Result re = (Result) out;
        re.setCode(1502);
        re.setMsg("修改正文发生异常");
    }
}
2.4 通过 BootStrap 拼装并执行
public class ArticleModifyExample1 {
    private final static Logger logger = LoggerFactory.getLogger(ArticleModifyExample1.class);

    public static void main(String[] args) {        //构造入参        
        ArticleTitleModifyCmd dto = new ArticleTitleModifyCmd();
        dto.setArticleId("articleId_001");
        dto.setTitle("articleId_001_title");
        dto.setContent("articleId_001_content");        //创建引导类        
        BootStrap bootStrap = new BootStrap();        //拼装并执行        
        Result result = (Result) bootStrap.inboundParameter(dto)//入参                
                .outboundFactory(new ResultFactory())//出参工厂                
                .channel(new ArticleModifyChannel())//自定义channel                
                .addChannelHandlerAtLast("checkParameter", new CheckParameterHandler())//第一个handler                
                .addChannelHandlerAtLast("modifyTitle", new ArticleModifyTitleHandler())//第二个handler                
                .addChannelHandlerAtLast("modifyContent", new ArticleModifyContentHandler())//第三个handler                
                .process();//执行        //result为执行结果        
        logger.info("result:code={},msg={}", result.getCode(), result.getMsg());
    }
}
2.5 执行结果

以下是运行 ArticleModifyExample1 的 main 方法打出的日志,可以看到我们定义的 handler 被逐个执行了。

3. 异常处理

3.1 Handler 异常处理

当某个Handler执行发生异常时,我们可将其异常处理逻辑实现在当前 Handler 的 exceptionCaught 方法中。

在 pie 案例工程( https://github.com/feiniaojin/pie-example.git )的 example2 包中,展示了某个 Handler 抛出异常时的处理方式。

假设 ArticleModifyTitleHandler 的业务逻辑会抛出异常,实例代码如下:

public class ArticleModifyTitleHandler implements ChannelHandler {
    private Logger logger = LoggerFactory.getLogger(ArticleModifyTitleHandler.class);

    @Override
    public void channelProcess(ChannelHandlerContext ctx, Object in, Object out) throws Exception {
        logger.info("修改标题:进入修改标题的Handler");
        ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in;
        String title = cmd.getTitle();        //此处的异常用于模拟执行过程中出现异常的场景        
        throw new RuntimeException("修改title发生异常");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception {
        logger.error("修改标题:异常处理逻辑");
        Result re = (Result) out;
        re.setCode(1501);
        re.setMsg("修改标题发生异常");
    }
}

此时 ArticleModifyTitleHandler 的 channelProcess 方法一定会抛出异常, 在当前 Handler 的 exceptionCaught 方法中对异常进行了处理。

运行 ArticleModifyExample2 的 main 方法,输出如下:

3.2 全局异常处理

有时候,我们不想每个 handler 都处理一遍异常,我们希望在执行链的最后统一进行处理。
在 ArticleModifyExample3 中,我们展示了通过一个全局异常进行最后的异常处理,其实现主要分为以下几步:

3.2.1 业务 Handler 传递异常

如果业务 Handler 实现了 ChannelHandler 接口,那么需要手工调用 ctx.fireExceptionCaught 方法向下传递异常。
例如 CheckParameterHandler 捕获到异常时的示例如下:

@Override
public class XXXHandler implements ChannelHandler {    //省略其他逻辑    //异常处理    
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception {
        logger.info("参数校验的异常处理逻辑:不处理直接向后传递");
        ctx.fireExceptionCaught(cause, in, out);
    }
}

如果业务 Handler 继承了 ChannelHandlerAdapter,如果没有重写 fireExceptionCaught 方法,则默认将异常向后传递。

3.2.2 实现全局异常处理的 Handler

我们把业务异常处理逻辑放到最后的 Handler 中进行处理,该 Handler 继承了ChannelHandlerAdapter,只需要重写异常处理的exceptionCaught
方法。
示例代码如下:

public class ExceptionHandler extends ChannelHandlerAdapter {
    private Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception {
        logger.error("异常处理器中的异常处理逻辑");
        Result re = (Result) out;
        re.setCode(500);
        re.setMsg("系统异常");
    }
}
3.2.3 将 ExceptionHandler 加入到执行链中

直接通过 BootStrap 加入到执行链最后即可,示例代码如下:

public class ArticleModifyExample3 {
    private final static Logger logger = LoggerFactory.getLogger(ArticleModifyExample3.class);

    public static void main(String[] args) {        //入参        
        ArticleTitleModifyCmd dto = new ArticleTitleModifyCmd();
        dto.setArticleId("articleId_001");
        dto.setTitle("articleId_001_title");
        dto.setContent("articleId_001_content");        //创建引导类        
        BootStrap bootStrap = new BootStrap();
        Result result = (Result) bootStrap.inboundParameter(dto)//入参                
                .outboundFactory(new ResultFactory())//出参工厂                
                .channel(new ArticleModifyChannel())//自定义channel                
                .addChannelHandlerAtLast("checkParameter", new CheckParameterHandler())//第一个handler                
                .addChannelHandlerAtLast("modifyTitle", new ArticleModifyTitleHandler())//第二个handler                
                .addChannelHandlerAtLast("modifyContent", new ArticleModifyContentHandler())//第三个handler                
                .addChannelHandlerAtLast("exception", new ExceptionHandler())//异常处理handler                
                .process();//执行        //result为执行结果        
        logger.info("result:code={},msg={}", result.getCode(), result.getMsg());
    }
}
3.2.4 运行 ArticleModifyExample3

运行 ArticleModifyExample3 的 main 方法,控制台输出如下,可以看到异常被传递到最后的 ExceptionHandler 中进行处理。

二、状态模式

状态机图

参考:告别复杂逻辑,项目终于用上了 Spring 状态机,非常优雅! (qq.com)

做需求时,需要了解以下六种元素:起始、终止、现态、次态(目标状态)、动作、条件,我们就可以完成一个状态机图了:

以订单为例:以从待支付状态转换为待发货状态为例

  • ①现态:是指当前所处的状态。待支付

  • ②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。支付事件

  • ③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。状态转换为待发货

  • ④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。待发货 注意事项

1、避免把某个“程序动作”当作是一种“状态”来处理。那么如何区分“动作”和“状态”?“动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;而“状态”是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。

2、状态划分时漏掉一些状态,导致跳转逻辑不完整。所以在设计状态机时,我们需要反复的查看设计的状态图或者状态表,最终达到一种牢不可破的设计方案。

状态模式框架

Spring Statemachine是应用程序开发人员在Spring应用程序中使用状态机概念的框架

Spring Statemachine旨在提供以下功能:

  1. 易于使用的扁平单级状态机,用于简单的使用案例。

  2. 分层状态机结构,以简化复杂的状态配置。

  3. 状态机区域提供更复杂的状态配置。

  4. 使用触发器,转换,警卫和操作。

  5. 键入安全配置适配器。

  6. 生成器模式,用于在Spring Application上下文之外使用的简单实例化通常用例的食谱

  7. 基于Zookeeper的分布式状态机

  8. 状态机事件监听器。

  9. UML Eclipse Papyrus建模。

  10. 将计算机配置存储在永久存储中。

  11. Spring IOC集成将bean与状态机关联起来。

状态机功能强大,因为行为始终保证一致,使调试相对容易。这是因为操作规则是在机器启动时写成的。这个想法是你的应用程序可能存在于有限数量的状态中,某些预定义的触发器可以将你的应用程序从一个状态转移到另一个状态。此类触发器可以基于事件或计时器。

在应用程序之外定义高级逻辑然后依靠状态机来管理状态要容易得多。您可以通过发送事件,侦听更改或仅请求当前状态来与状态机进行交互。

1)引入依赖

 <!-- redis持久化状态机 -->  
    <dependency>  
        <groupId>org.springframework.statemachine</groupId>  
        <artifactId>spring-statemachine-redis</artifactId>  
        <version>1.2.9.RELEASE</version>  
    </dependency>  
    <!--状态机-->  
    <dependency>  
        <groupId>org.springframework.statemachine</groupId>  
        <artifactId>spring-statemachine-starter</artifactId>  
        <version>2.0.1.RELEASE</version>  
    </dependency>  

2)定义状态机状态和事件

状态枚举:

public enum OrderStatus {  
        // 待支付,待发货,待收货,已完成  
        WAIT_PAYMENT(1, "待支付"),  
        WAIT_DELIVER(2, "待发货"),  
        WAIT_RECEIVE(3, "待收货"),  
        FINISH(4, "已完成");  
        private Integer key;  
        private String desc;  
        OrderStatus(Integer key, String desc) {  
            this.key = key;  
            this.desc = desc;  
        }  
        public Integer getKey() {  
            return key;  
        }  
        public String getDesc() {  
            return desc;  
        }  
        public static OrderStatus getByKey(Integer key) {  
            for (OrderStatus e : values()) {  
                if (e.getKey().equals(key)) {  
                    return e;  
                }  
            }  
            throw new RuntimeException("enum not exists.");  
        }  
    }  

事件:

public enum OrderStatusChangeEvent {  
        // 支付,发货,确认收货  
        PAYED, DELIVERY, RECEIVED;  
}  

3)定义状态机规则和配置状态机

 @Configuration  
    @EnableStateMachine(name = "orderStateMachine")  
    public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {  
        /**  
         * 配置状态  
         *  
         * @param states  
         * @throws Exception  
         */  
        public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {  
            states  
                    .withStates()  
                    .initial(OrderStatus.WAIT_PAYMENT)  
                    .states(EnumSet.allOf(OrderStatus.class));  
        }  
        /**  
         * 配置状态转换事件关系  
         *  
         * @param transitions  
         * @throws Exception  
         */  
        public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception {  
            transitions  
                    //支付事件:待支付-》待发货  
                    .withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)  
                    .and()  
                    //发货事件:待发货-》待收货  
                    .withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)  
                    .and()  
                    //收货事件:待收货-》已完成  
                    .withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);  
        }  
    }  

配置持久化:

@Configuration  
@Slf4j  
public class Persist<E, S> {  
    /**  
     * 持久化到内存map中  
     *  
     * @return  
     */  
    @Bean(name = "stateMachineMemPersister")  
    public static StateMachinePersister getPersister() {  
        return new DefaultStateMachinePersister(new StateMachinePersist() {  
            @Override  
            public void write(StateMachineContext context, Object contextObj) throws Exception {  
                log.info("持久化状态机,context:{},contextObj:{}", JSON.toJSONString(context), JSON.toJSONString(contextObj));  
                map.put(contextObj, context);  
            }  
            @Override  
            public StateMachineContext read(Object contextObj) throws Exception {  
                log.info("获取状态机,contextObj:{}", JSON.toJSONString(contextObj));  
                StateMachineContext stateMachineContext = (StateMachineContext) map.get(contextObj);  
                log.info("获取状态机结果,stateMachineContext:{}", JSON.toJSONString(stateMachineContext));  
                return stateMachineContext;  
            }  
            private Map map = new HashMap();  
        });  
    }  
  
    @Resource  
    private RedisConnectionFactory redisConnectionFactory;  
    /**  
     * 持久化到redis中,在分布式系统中使用  
     *  
     * @return  
     */  
    @Bean(name = "stateMachineRedisPersister")  
    public RedisStateMachinePersister<E, S> getRedisPersister() {  
        RedisStateMachineContextRepository<E, S> repository = new RedisStateMachineContextRepository<>(redisConnectionFactory);  
        RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository);  
        return new RedisStateMachinePersister<>(p);  
    }  
}  

4)业务系统

controller:

@RestController  
@RequestMapping("/order")  
public class OrderController {  
    @Resource  
    private OrderService orderService;  
    /**  
     * 根据id查询订单  
     *  
     * @return  
     */  
    @RequestMapping("/getById")  
    public Order getById(@RequestParam("id") Long id) {  
        //根据id查询订单  
        Order order = orderService.getById(id);  
        return order;  
    }  
    /**  
     * 创建订单  
     *  
     * @return  
     */  
    @RequestMapping("/create")  
    public String create(@RequestBody Order order) {  
        //创建订单  
        orderService.create(order);  
        return "sucess";  
    }  
    /**  
     * 对订单进行支付  
     *  
     * @param id  
     * @return  
     */  
    @RequestMapping("/pay")  
    public String pay(@RequestParam("id") Long id) {  
        //对订单进行支付  
        orderService.pay(id);  
        return "success";  
    }  
  
    /**  
     * 对订单进行发货  
     *  
     * @param id  
     * @return  
     */  
    @RequestMapping("/deliver")  
    public String deliver(@RequestParam("id") Long id) {  
        //对订单进行确认收货  
        orderService.deliver(id);  
        return "success";  
    }  
    /**  
     * 对订单进行确认收货  
     *  
     * @param id  
     * @return  
     */  
    @RequestMapping("/receive")  
    public String receive(@RequestParam("id") Long id) {  
        //对订单进行确认收货  
        orderService.receive(id);  
        return "success";  
    }  
}  

servie:

 @Service("orderService")  
 @Slf4j  
 public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {  
     @Resource  
     private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;  
     @Resource  
     private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;  
     @Resource  
     private OrderMapper orderMapper;  
     /**  
      * 创建订单  
      *  
      * @param order  
      * @return  
      */  
     public Order create(Order order) {  
         order.setStatus(OrderStatus.WAIT_PAYMENT.getKey());  
         orderMapper.insert(order);  
         return order;  
     }  
     /**  
      * 对订单进行支付  
      *  
      * @param id  
      * @return  
      */  
     public Order pay(Long id) {  
         Order order = orderMapper.selectById(id);  
         log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);  
         if (!sendEvent(OrderStatusChangeEvent.PAYED, order)) {  
             log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);  
             throw new RuntimeException("支付失败, 订单状态异常");  
         }  
         return order;  
     }  
     /**  
      * 对订单进行发货  
      *  
      * @param id  
      * @return  
      */  
     public Order deliver(Long id) {  
         Order order = orderMapper.selectById(id);  
         log.info("线程名称:{},尝试发货,订单号:{}" ,Thread.currentThread().getName() , id);  
         if (!sendEvent(OrderStatusChangeEvent.DELIVERY, order)) {  
             log.error("线程名称:{},发货失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);  
             throw new RuntimeException("发货失败, 订单状态异常");  
         }  
         return order;  
     }  
     /**  
      * 对订单进行确认收货  
      *  
      * @param id  
      * @return  
      */  
     public Order receive(Long id) {  
         Order order = orderMapper.selectById(id);  
         log.info("线程名称:{},尝试收货,订单号:{}" ,Thread.currentThread().getName() , id);  
         if (!sendEvent(OrderStatusChangeEvent.RECEIVED, order)) {  
             log.error("线程名称:{},收货失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);  
             throw new RuntimeException("收货失败, 订单状态异常");  
         }  
         return order;  
     }  
     /**  
      * 发送订单状态转换事件  
      * synchronized修饰保证这个方法是线程安全的  
      *  
      * @param changeEvent  
      * @param order  
      * @return  
      */  
     private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {  
         boolean result = false;  
         try {  
             //启动状态机  
             orderStateMachine.start();  
             //尝试恢复状态机状态  
             stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));  
             Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();  
             result = orderStateMachine.sendEvent(message);  
             //持久化状态机状态  
             stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));  
         } catch (Exception e) {  
             log.error("订单操作失败:{}", e);  
         } finally {  
             orderStateMachine.stop();  
         }  
         return result;  
     }  
 }  

监听状态的变化:

@Component("orderStateListener")  
@WithStateMachine(name = "orderStateMachine")  
@Slf4j  
public class OrderStateListenerImpl {  
    @Resource  
    private OrderMapper orderMapper;  
      
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")  
    public void payTransition(Message<OrderStatusChangeEvent> message) {  
        Order order = (Order) message.getHeaders().get("order");  
        log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());  
        //更新订单  
        order.setStatus(OrderStatus.WAIT_DELIVER.getKey());  
        orderMapper.updateById(order);  
        //TODO 其他业务  
    }  
    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")  
    public void deliverTransition(Message<OrderStatusChangeEvent> message) {  
        Order order = (Order) message.getHeaders().get("order");  
        log.info("发货,状态机反馈信息:{}",  message.getHeaders().toString());  
        //更新订单  
        order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());  
        orderMapper.updateById(order);  
        //TODO 其他业务  
    }  
    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")  
    public void receiveTransition(Message<OrderStatusChangeEvent> message) {  
        Order order = (Order) message.getHeaders().get("order");  
        log.info("确认收货,状态机反馈信息:{}",  message.getHeaders().toString());  
        //更新订单  
        order.setStatus(OrderStatus.FINISH.getKey());  
        orderMapper.updateById(order);  
        //TODO 其他业务  
    }  
}  

状态机存在无法抛出异常的问题

stateMachine无法抛出异常,异常会被状态机给消化掉

问题现象

orderStateMachine.sendEvent(message);获取的结果无法感知到。无论执行正常还是抛出异常,都返回true。调试发现:发送事件和监听事件是一个线程,发送事件的结果是在监听操作执行完之后才返回

解决方法:

1. 自己保存异常到数据库或者内存中,进行判断
2. 通过状态机接口

org.springframework.statemachine.StateMachine##getExtendedState

方法把执行状态放入这个变量中

//获取到监听的结果信息  
Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());  
  • 监听设置状态的代码有重复代码,需要进行优化,可使用aop

try {  
        //TODO 其他业务  
        //成功 则为1  
        orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);  
    } catch (Exception e) {  
        //如果出现异常,则进行回滚  
        log.error("payTransition 出现异常:{}",e);  
        //将异常信息变量信息中,失败则为0  
        orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);  
        throw e;  
    }  

常量类:

public interface CommonConstants {  
        String orderHeader="order";  
        String payTransition="payTransition";  
        String deliverTransition="deliverTransition";  
        String receiveTransition="receiveTransition";  
    }  

支付发送事件:com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##pay

@Resource  
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;  
@Resource  
private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;  
@Resource  
private OrderMapper orderMapper;  
  
/**  
 * 对订单进行支付  
 *  
 * @param id  
 * @return  
 */  
public Order pay(Long id) {  
    Order order = orderMapper.selectById(id);  
    log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);  
    if (!sendEvent(OrderStatusChangeEvent.PAYED, order,CommonConstants.payTransition)) {  
        log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);  
        throw new RuntimeException("支付失败, 订单状态异常");  
    }  
    return order;  
}  
  
/**  
 * 发送订单状态转换事件  
 * synchronized修饰保证这个方法是线程安全的  
 *  
 * @param changeEvent  
 * @param order  
 * @return  
 */  
private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order,String key){  
    boolean result = false;  
    try {  
        //启动状态机  
        orderStateMachine.start();  
        //尝试恢复状态机状态  
        stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));  
        Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();  
        result = orderStateMachine.sendEvent(message);  
        if(!result){  
            return false;  
        }  
        //获取到监听的结果信息  
        Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(key + order.getId());  
        //操作完成之后,删除本次对应的key信息  
        orderStateMachine.getExtendedState().getVariables().remove(key+order.getId());  
        //如果事务执行成功,则持久化状态机  
        if(Objects.equals(1,Integer.valueOf(o))){  
            //持久化状态机状态  
            stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));  
        }else {  
            //订单执行业务异常  
            return false;  
        }  
    } catch (Exception e) {  
        log.error("订单操作失败:{}", e);  
    } finally {  
        orderStateMachine.stop();  
    }  
    return result;  
}  
3.使用aop切面

把业务执行结果封装到状态机的变量中,注解:

@Retention(RetentionPolicy.RUNTIME)  
public @interface LogResult {  
    /**  
     *执行的业务key  
     *  
     * @return String  
     */  
    String key();  
}  

切面:

@Component  
@Aspect  
@Slf4j  
public class LogResultAspect {  
  
    //拦截 LogHistory注解  
    @Pointcut("@annotation(com.zengqingfa.springboot.state.demo.aop.annotation.LogResult)")  
    private void logResultPointCut() {  
        //logResultPointCut 日志注解切点  
    }  
    @Resource  
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;  
      
    @Around("logResultPointCut()")  
    public Object logResultAround(ProceedingJoinPoint pjp) throws Throwable {  
        //获取参数  
        Object[] args = pjp.getArgs();  
        log.info("参数args:{}", args);  
        Message message = (Message) args[0];  
        Order order = (Order) message.getHeaders().get("order");  
        //获取方法  
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();  
        // 获取LogHistory注解  
        LogResult logResult = method.getAnnotation(LogResult.class);  
        String key = logResult.key();  
        Object returnVal = null;  
        try {  
            //执行方法  
            returnVal = pjp.proceed();  
            //如果业务执行正常,则保存信息  
            //成功 则为1  
            orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 1);  
        } catch (Throwable e) {  
            log.error("e:{}", e.getMessage());  
            //如果业务执行异常,则保存信息  
            //将异常信息变量信息中,失败则为0  
            orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 0);  
            throw e;  
        }  
        return returnVal;  
    }  
}  

监听类使用注解:

@Component("orderStateListener")  
@WithStateMachine(name = "orderStateMachine")  
@Slf4j  
public class OrderStateListenerImpl {  
    @Resource  
    private OrderMapper orderMapper;  
  
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")  
    @Transactional(rollbackFor = Exception.class)  
    @LogResult(key = CommonConstants.payTransition)  
    public void payTransition(Message<OrderStatusChangeEvent> message) {  
        Order order = (Order) message.getHeaders().get("order");  
        log.info("支付,状态机反馈信息:{}", message.getHeaders().toString());  
        //更新订单  
        order.setStatus(OrderStatus.WAIT_DELIVER.getKey());  
        orderMapper.updateById(order);  
        //TODO 其他业务  
        //模拟异常  
        if (Objects.equals(order.getName(), "A")) {  
            throw new RuntimeException("执行业务异常");  
        }  
    }  
    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")  
    @LogResult(key = CommonConstants.deliverTransition)  
    public void deliverTransition(Message<OrderStatusChangeEvent> message) {  
        Order order = (Order) message.getHeaders().get("order");  
        log.info("发货,状态机反馈信息:{}", message.getHeaders().toString());  
        //更新订单  
        order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());  
        orderMapper.updateById(order);  
        //TODO 其他业务  
    }  
    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")  
    @LogResult(key = CommonConstants.receiveTransition)  
    public void receiveTransition(Message<OrderStatusChangeEvent> message) {  
        Order order = (Order) message.getHeaders().get("order");  
        log.info("确认收货,状态机反馈信息:{}", message.getHeaders().toString());  
        //更新订单  
        order.setStatus(OrderStatus.FINISH.getKey());  
        orderMapper.updateById(order);  
        //TODO 其他业务  
    }  
}

三、观察者模式 

天气对象和界面组件之间的依赖关系就可以用观察者模式实现,该天气对象就是目标对象,天气数据就是它的状态。

这些界面组件就是观察者对象,当天气对象获取到新的天气数据时(此时它的状态改变了),它就通知所有依赖于它的界面组件,这些组件就更新它们显示的内容。

观察者模式框架

1) 引入依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>29.0-jre</version>
</dependency>


引入依赖后,这里我们主要使用 com.google.common.eventbus.EventBus 类进行操作,其提供了 register、unregister、post 来进行注册订阅、取消订阅和发布消息

public void register(Object object);

public void unregister(Object object);

public void post(Object event);


2). 同步使用


首先创建一个 EventBus

EventBus eventBus = new EventBus();



3)创建一个订阅者


在 Guava EventBus 中,是根据参数类型进行订阅,每个订阅的方法只能有一个参数,同时需要使用 @Subscribe 标识

class EventListener {

  /**
   * 监听 Integer 类型的消息
   */
  @Subscribe
  public void listenInteger(Integer param) {
    System.out.println("EventListener#listenInteger ->" + param);
  }

  /**
   * 监听 String 类型的消息
   */
  @Subscribe
  public void listenString(String param) {
    System.out.println("EventListener#listenString ->" + param);
  }
}


4)注册到 EventBus 上并发布消息

EventBus eventBus = new EventBus();

eventBus.register(new EventListener());

eventBus.post(1);
eventBus.post(2);
eventBus.post("3");


运行结果为

EventListener#listenInteger ->1
EventListener#listenInteger ->2
EventListener#listenString ->3


根据需要我们可以创建多个订阅者完成订阅信息,同时如果一个类型存在多个订阅者,则所有订阅方法都会执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值