【PostConstruct】(二)多业务线合并

      根据上一篇文章所述,得知@PostConstruct注解修饰的方法会在构造方法执行之后调用,这篇文章就来阐述下,这个特性的一个使用场景。


场景

      假设订单系统对外封装了一套完整的RPC服务(比如一套“贷款”流程),这套流程主要有从“意向创建”到“还款”7个流程(每个流程其实对应一套接口),同时,“贷款流程”内部又有并行的3条业务线,比如“Backward”、“Forward”、“ThirdWard”,为方便理解, 见下图:

        

      由于我设计的“贷款流程”有7个步骤,同时3条业务线,按理来说我就需要写21个RPC接口了,显然很不满足OOD的思想,这时@PostConstruct就可以派上用场,只要7个接口就完全搞定,用“提交订单”这一步骤举例,请见下文。


详情

      假设“提交订单”这一个步骤中,对应的3条业务向都可能会有“校验”、“提交”、“发送资质审核”、“发送操作记录”这4个子流程,通过抽取接口和抽象类,整理了类图如下:

          

      从顶向下看:

     (1)通过7步流程,抽象除了ISubmitService接口,其中只暴露submit()方法。

     (2)对3条业务线进行抽象,AbsSubmitService类抽象出了共有属性hasMap,

数据类型为Map<Integer,AbsSubmitService>(这个属性就是关键),以及对应的子类抽象出的方法,和自己特有的私有方法initChildren().

     (3)个具体实现类中分别对应了:

      1、checkBusinessDate()

      2、execSubmit()

      3、sendQualAudit()

      4、sendHistoryRecord()

      这4个方法,只是各自的实现有差异。

      那么到底@PostConstruct是怎么派上用场的,了解了上述业务结构以及接口、类图设计之后,参考我设计的伪代码:


代码展示

      1.接口

public interface ISubmitService {
    /**
     * 订单提交
     * @param form
     * @return
     * @throws BusinessException
     */
    Integer submit(ApplyForm form) throws BusinessException;
}

      2.抽象类(important)

public class AbsSubmitService implements ISubmitService{
	
    private static final Logger logger = LoggerFactory.getLogger(AbsSubmitService.class);
	
    /*@Autowired                   
    protected ApplyDao applyDao;*/ 
	
    public static Map<Integer, AbsSubmitService> hashMap = new ConcurrentHashMap<>();//存放实体bean
	
    @PostConstruct
    protected void initChildren(){
        hashMap.put(getType(),this);
    }
	
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public Integer submit(final ApplyForm form) throws BusinessException {
    	//step1:校验
        checkBusinessData(form);
        
        //step2:提交
        execSubmit(form);
        
        //step3:发送资质审核
         sendQualAudit(form);
         
        //step4:发送操作记录
        sendHistoryRecordContent(form.getId());
        return 1;
    }
    
    
    /**
     * 获取类型
     * @return
     */
    abstract Integer getType();

    /**
     * step1:校验业务
     * @param form
     * @throws BusinessException
     */
    abstract void checkBusinessData(MMCLoanApplyForm form) throws BusinessException;

    
    /**
     * step2:提交
     * @param form
     * @return
     */
    abstract Integer execSubmit(MMCLoanApplyForm form)throws BusinessException;

    /**
     * step3:发送资质审核
     * @param form
     * @throws BusinessException
     */
    abstract void sendQualAudit(MMCLoanApplyForm form) throws BusinessException;

    /**
     * step4:发送操作记录
     * @param form
     * @throws BusinessException
     */
    protected void sendHistoryRecordContent(Long applyId, Boolean isNew) {
    	//todo
    }
}

      3.Backward实现类

public class BackwardSubmitServer extends AbsSubmitService{
	
    private static final Logger logger = LoggerFactory.getLogger(AbsSubmitService.class);

    @Override
    Integer getType() {
	logger.error("伪代码:反向业务--获取类型");
        return BusinessFlowEnum.BackWard.getIndex();
    }
	
    @Override
    Integer execSubmit(ApplyForm form) throws BusinessException {
        logger.error("伪代码:反向业务--提交");
        //todo 反向提交
    }
	
    @Override
    void sendQualAudit(ApplyForm form) throws BusinessException {
	logger.error("伪代码:反向业务--发送资质审核");
        //todo 反向发送资质审核
    }
	
    @Override
    void checkBusinessData(MMCLoanApplyForm form) throws BusinessException {
	logger.error("伪代码:反向业务--校验业务");
        //todo 反向校验
    }
}

      4.Forward实现类

public class ForwardSubmitServer extends AbsSubmitService{
	
    private static final Logger logger = LoggerFactory.getLogger(AbsSubmitService.class);
	
    @Override
    Integer getType() {
	logger.error("伪代码:正向业务--获取类型");
        return BusinessFlowEnum.Forward.getIndex();
    }
	
    @Override
    Integer execSubmit(ApplyForm form) throws BusinessException {
        logger.error("伪代码:正向业务--提交");
        //todo 正向提交
    }
	
    @Override
    void sendQualAudit(ApplyForm form) throws BusinessException {
	logger.error("伪代码:正向业务--发送资质审核");
        //todo 正向发送资质审核
    }
	
    @Override
    void checkBusinessData(MMCLoanApplyForm form) throws BusinessException {
	logger.error("伪代码:正向业务--校验业务");
        //todo 正向校验
    }
}

      5.Thirdward实现类

public class ThirdSubmitServer extends AbsSubmitService{
	
    private static final Logger logger = LoggerFactory.getLogger(AbsSubmitService.class);

    @Override
    Integer getType() {
	logger.error("伪代码:正向业务--获取类型");
        return BusinessFlowEnum.THIRD.getIndex();
    }
	
    @Override
    Integer execSubmit(ApplyForm form) throws BusinessException {
        logger.error("伪代码:正向业务--提交");
        //todo 正向提交
    }
	
    @Override
    void sendQualAudit(ApplyForm form) throws BusinessException {
	logger.error("伪代码:正向业务--发送资质审核");
        //todo 正向发送资质审核
    }
	
    @Override
    void checkBusinessData(MMCLoanApplyForm form) throws BusinessException {
	logger.error("伪代码:正向业务--校验业务");
        //todo 正向校验
    }
}

      6.业务标识枚举

/**
 * 标识业务线的枚举
 * @author zhenhua.zhang
 */
public enum BusinessFlowEnum {
    Forward(1,"正向业务"),
    BackWard(2,"反向业务"),
    THIRD(3,"三方业务");

    private int index;
    private String name;

    BusinessFlowEnum(int index, String name) {
        this.index = index;
        this.name = name;
    }

    @Override
    public int getIndex() {
        return index;
    }

    @Override
    public String getName() {
        return name;
    }

    public static BusinessFlowEnum get(int index) {
        for (BusinessFlowEnum e : BusinessFlowEnum.values()) {
            if (e.getIndex() == index) {
                return e;
            }
        }
        return null;
    }
    public static String getNameByIndex(int index) {
    	BusinessFlowEnum node = get(index);
        return node == null ? null : node.getName();
    }
}

      如上所述,基本结构出来了,注意抽象类AbsSubmitService中的这句:

public static Map<Integer, AbsSubmitService> hashMap = new ConcurrentHashMap<>();//存放实体bean

      如果读过spring源码就会发现,“AbstractBeanFactory”中有类似的设计:

private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();  

      这不就是IOC吗?对,这就是容器的思想,AbsSubmitService中的hasMap变量就是一个容器,用于装3条业务线的具体实现,再看被@PostConstruct修饰的“initChildren”方法:

@PostConstruct
protected void initChildren(){
   hashMap.put(getType(),this);
}

      用来向hasMap变量中通过getType()放入具体的子类实现,此处设计来源,参考spring源码:

@Override  
public Object getBean(String name) throws Exception {  
	BeanDefinition beanDefinition = beanDefinitionMap.get(name);  //这里哦
	if (beanDefinition == null) {  
		throw new IllegalArgumentException("No bean named " + name + " is defined");  
	}  
	Object bean = beanDefinition.getBean();   
	return bean;  
} 

      通过getType()在3个子类中的实现,来把3个子类往AbsSubmitService中加载:

@Override
Integer getType() {
     logger.error("伪代码:正向业务--获取类型");
     return BusinessFlowEnum.Forward.getIndex(); //比如正向业务的实现类获取是通过这句
}


      So,到此大致原理就都解释清楚了,那么@PostConstruct主角出场,当AbsSubmitService的构造方法执行完成之后,开始加载被@PostConstruct修饰的initChildren(),由于getType()是抽象方法,执行具体实现中的getType()方法,把3个子类加载到hashMap容器当中,此时就可以开始调用啦,下面参考我写的Test伪代码:

public class Test {

    public static void main(String[] args) {
	//1.实例化form,并赋值
	//todo-伪代码
	//2.调用sub方法
	Integer result = submit(form,productTypeId);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public Integer submit(final ApplyForm form, final Integer productTypeId) throws BusinessException {
        try {
            //加入分布式锁约束
            CreateOrderRedisLock.getInstance().executeWithLock(form.getSaleOrderNo(), new RedisLockCallback() {
                @Override
                public void execute() throws BusinessException {
                     AbsSubmitService.hashMap.get(productTypeId).submit(form);
                }
            });
        } catch (BusiLockedException e) {
            logger.error("提交功能异常",e.getMessage());
            throw new BusinessException("请稍后再试!");
        }
        return 1;
    }
}

        加入了分布式锁之后,在样板代码中调用SubmitService中的submit()方法:

AbsSubmitService.hashMap.get(productTypeId).submit(form);

        根据接口传入的typeId(结合我写的枚举类),就会在3条业务线中找到对应的方法,提交订单了。


Summary

       通过“订单提交”这一个伪代码例子,详细阐述了@PostConstruct的具体场景应用,做到了在3条业务线中最终只提供1个RPC接口的例子,其余6个接口也可以这样搞,为了方便,可以在这个基础上再次抽象一层,如下伪代码:

public interface IBusinessFlow {
	/**
	 * step1 创建意向
	 */
	void createWishOrder();
	
	/**
	 * step2 审核风控
	 */
	void audit();
	
	/**
	 * step3 下单
	 */
	void order();
	
	/**
	 * step4 完善资料
	 */
	void complete();
	
	/**
	 * step5 提交订单
	 */
	void submit();
	
	/**
	 * step6 放款
	 */
	void loan();
	
	/**
	 * step7 还款
	 */
	void repay();
}

      最终在IDE中,目录的结构也因此划分而非常清晰。

      五一快乐!

      That's all. 

     

       

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值