较多业步骤场景通用框架

本文探讨了在处理复杂业务如创建订单时,如何通过将校验逻辑设计为可插拔组件,利用Spring Boot的生命周期,以及多态来提升代码可维护性和性能。介绍了将校验逻辑拆分为单独组件、使用ThreadLocal优化数据共享以及运用多态扩展业务处理类的方法,以应对业务需求变化带来的挑战。
摘要由CSDN通过智能技术生成

我们工作的大部分时间都在写业务代码,如何写好业务代码必然是我们追求的一大目标,在编程方面,简单、易懂、可扩展性是衡量代码质量的通用标准,所以在工作中,我们能用java将产品经理的想法表达出来还不够,我们产出的内容最好还能让其它的工程师一目了然。

本文以创建订单这个业务场景着手,提供一种通用完成创建和编辑业务对象的模式。此业务发起是从前端的一个post请求,就假设为 /api/v1/order,后端通过 OrderController控制层接口,然后由 OrderService处理相关的业务逻辑,再通过OrderRepository持久层入库,请求处理完之后返回给前端一个orderUuid,这是一种很常见的业务:
在这里插入图片描述

对于Controller层,大多数情况下只用于构建参数和VO,以及及少量的参数校验(例如非空校验,格式校验等),多数的校验包含业务性的,有时需要与其它的微服务进行交互,我们通常会将这些校验移到service层,repository层只用于数据持久化,这里不做过多讨论,变化后的流程图如下:
在这里插入图片描述

流程图一完成,业务代码轻车熟路,仅看service层的代码实现,省略其它

@Service
public class OrderService {
   
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public OrderDTO createOrder(OrderCreateRequest request) {
   
        // 校验1:用户是否有权限
        checkoutUserAuthorization(request);
        // 校验2:产品是否还有库存, 产品状态等
        checkoutProduct(request);
        
        final Order save = orderRepository.save();
        
        // 发mq消息给其它系统
        sendMqMessage(save);
        // 发邮件
        // sendEmail();
        
        // 记录操作日志
        // logService.saveLog()...
        
        return convert(save);
    }

    private void checkoutUserAuthorization(OrderCreateRequest request) {
   
        // ....查 userService等等
    }

    private void checkoutProduct(OrderCreateRequest request) {
   
        // ...查 productService,处理业务
    }
    
    private void sendMqMessage(Order order) {
   
        // 调mqService发消息
    }

    private OrderDTO convert(Order save) {
   
        OrderDTO dto = new OrderDTO();
        BeanUtils.copyProperties(save, dto);
        return dto;
    }
}

可能绝大多数工程师能够熟练并且无误地完成以上的代码编写工作,而这也是我们最常用的设计模式(MVC设计模式),当然也是最简单且和最高效的编码方式。那么到现在停下来想一想,这样写的代码有什么问题?

我能想到以下问题:

  1. 如果是简单的业务,这段代码没有什么问题,也值得推荐。但订单业务是很复杂的,一是校验可能很多很严格;二与其交互的微服务或第三方系统也不少;三是可能根据订单的种类,有不同的校验方法,那么代码中就会有很多if ... else
  2. 如果业务非常复杂,这项工作要由多人共同完成,那么大家都会到这个类中编写代码,OrderService就成了一个极其臃肿的类,很快代码就超过五六百行(大家可以想想自己平时见到一个类代码有五百行时的感受)。
  3. 性能问题,假设在校验的时候通过调用userService查了当前用户信息,校验完成之后走到后面,记录日志的时候又要用当前用户信息,如果不缓存就又要查一遍,缓存的话就增加了系统的复杂度。
  4. 一旦代码结构这样设计了,那么使用面向对象的思想就终结了,这也是我觉得最重要的一点。

spring框架给我们提供了很多便利,但是要警惕这些便利的诱惑,我们可以很容易将一个类定义成一个单例bean,并将它交给spring容器进行管理,但是一旦这么做了,在容器启动过程中就创建了这个类的单实例,那么这个对象就必定是一种无状态的,那它本身就成为了一个工具类,处理业务逻辑的工具,它能对外提供一系列方法来处理业务逻辑,我们只需要使用Autowired注解注入这个对象,就能在很多地方随意的使用它提供的方法,仅此而已,这时我们已经坠入了面向过程编辑的陷井。


那么如何解决以上问题呢?

一、校验逻辑又多又杂,需要设计成可插拔的组件。每个组件一个类,这样可以由多人独立开发,并且代码不会冗余到OrderService类中。

参考spring在生产bean的过程中有很多生命周期,而在每个生命周期的处理逻辑上,都使用了不同的 BeanPostProcessor,每个postProcessor都是可扩展的,对它的扩展不会影响spring创建 bean的主流程。简单点来说,增加或减少一个校验逻辑,OrderService的代码不会有任何改动。

下面是spring初始化bean的核心逻辑:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
   
        if (System.getSecurityManager() != null) {
   
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
   
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
   
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
   
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
   
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
   
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
   
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

这段代码做了什么事呢?

  1. 调用所有的 invokeAware方法,比如正在创建 UserService,而 UserService 实现了 BeanFactoryAware 接口。那么在此处,spring 就会主动调用UserService里面的 setBeanFactory()方法。
  2. 找到容
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值