一起探讨下web请求流程的代码结构设计(简单以交易为栗子)

748 阅读7分钟

今天这篇文章所涉及的相关观点做法,按照我们公司某位架构师的说法就是,架构这东西没有对错之分,集思广益,不一定我的做法就是对的,不一定我的做法就是最优的,小编和大家一起探讨探讨。

一、场景

首先我们模拟一个SDK交易链路的场景,对交易不是很懂的同学可以先移步至微信支付了解下大概业务,其实小编也不是特别精通交易,刚实习,但这不影响本文哈!一笔交易的生命周期大致可以分为这么几个阶段,即预下单---支付---交易通知,什么意思呢?拿微信扫码点餐付款来说,首先微信客户端扫码之后会先生成一笔订单,当预下单完成之后,客户端会触发SDK发起支付请求,然后用户输入密码完成支付,支付之后会发送订单状态通知用户,我们最后才能知道我到底付款成功没有。

下面是微信扫码支付业务时序图:

时序图

既然业务步骤是明确的,我们首先考虑代码方面应该怎么去架设会好一点,首先猜想能否定义三个生命周期方法,如下:

// 前置业务---预下单
beforeService()
// 核心业务处理----支付
doService()
// 后置业务处理----通知
afterService()

但是,实际业务每个步骤往往非常复杂,也就说比如我们有可能在预下单的时候会加入优惠信息,判断当前用户这笔订单是否符合优惠,如果优惠后的金额为0,还有可能不需要任何的支付处理,直接就发送通知给到用户。还有一点我们业务通常还要区别是刷卡预下单还是扫码预下单,扫码预下单还要区分是主扫还是被扫,因为这几种区分是实际和线下场景相匹配的,为了降低耦合,这时我们会分别提供一个接口来处理各种请求。

既然是每种场景对应一个接口,我们又可以想到,通常接口都是需要进行安全性校验的,如MD5、RSA及SHA1各种加密,保证交易的安全性。为了和核心业务区分开来,我们通常会在前置业务中甚至在filter端就把这些校验就完成掉,避免如果校验出错进到业务层去。既然每个接口都有诸如前置校验----核心处理----后置处理的逻辑,那我们可以把上面的设想再细分一下,即:

// 前置业务---参数校验,加密等操作
beforeService()
// 核心业务处理----预下单、支付等核心业务
doService()
// 后置业务处理----保存订单、保存流水、发送通知等后置业务
afterService()

那么代码层应该如何去设计呢?可否这样,像下面的UML图:

下面对各个组件进行讲解:

  • SDKService

    最顶层的抽象,一般业务类不需要实现它,需要需要扩展该接口,直接继承即可。

  • SDKNoCardService

    无卡交易相关能力,扩展SDKService

  • SDKRomoteService

    调用远程方法交易相关能力,扩展SDKService,如远程调用微信下单接口

  • AbstractSDKService

    抽象类,实现SDKService,同时定义上面的三个声明周期方法,相当于一个模板,具体实现由子类去重写即可

  • SDKNoCardServceImpl

    无卡交易业务实现类,继承抽象类,因为需要获取卡号,所以实现SDKNoCardService接口

  • SDKCardTradeServiceImpl

    刷卡交易业务实现类,继承抽象类,因为需要调用远程服务,所以实现SDKRomoteService接口

这样做的好处在于,如果我需要引入一个新的需求,比如说小额免签免密,那么我只需要创建一个新类继承抽象类AbstractSDKService即可,如果小额免签免密需要一个新的能力,那么只需要新增一个接口扩展SDKService即可。下面给出相应的测试代码:

SDKService.java

package com.example.interfaces;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

public interface SDKService<T,R> {
    /**
    * 业务处理
    * @param request
    * 					请求
    * 
    * @return
    */
    public R service(HttpServletRequest request);
    
    /**
    * 业务处理
    * @param body
    * 					客户端请求消息体对象
    * 
    * @param request
    * 					请求
    * 
    * @return
    */
    public R service(T body, HttpServletRequest request);
    
    /**
    * 业务处理
    * @param body
    * 					客户端请求消息体对象
    * 
    * @param headers
    * 					客户端请求头信息对象
    * 
    * @return
    */
    public R service(T body, Map<String, String> headers);
}

SDKRemoteService.java

package com.example.interfaces;

public interface SDKRemoteService<T, R> extends SDKService<T, R>{
    
    /**
     * 调用远程交易,比如说调用微信支付
     * @return
     */
    public R call();
}

SDKNoCardService.java

package com.example.interfaces;

public interface SDKNoCardService<T, R> extends SDKService<T, R> {
    /**
     * 获取卡号
     * @return
     */
    public R getCardNo();
}

AbstractSDKService.java

package com.example.interfaces;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

/**
 * 传入参数T,返回结果R
 * 
 * @author huangjiawei
 *
 * @param <T>
 * @param <R>
 */
public abstract class AbstractSDKService<T, R> implements SDKService<T, R> {
    
    // 前置处理,比如可以对参数进行校验,由子类选择性重写
    protected void beforeService() {
    };
    
    /**
     * 核心业务方法,由不同业务子类选择性去重写
     * 
     * @return
     */
    protected abstract R doService();
    
    // 后置处理,比如记录业务结果,由子类选择性重写
    protected void afterService() {
    };
    
    /**
     * 接受请求
     */
    @Override
    public R service(HttpServletRequest request) {
    	// 模拟请求体
    	System.err.println("service with only request!");
    	return this.service(null, request);
    }
    
    /**
     * 处理请求体
     */
    @Override
    public R service(T body, HttpServletRequest request) {
    	// 模拟请求体
    	Map<String, String> headers = new HashMap<>();
    	System.err.println("service with body and request!");
    	return this.service(body, headers);
    }
    
    /**
     * 处理请求头和实际业务逻辑
     */
    @Override
    public R service(T body, Map<String, String> headers) {
    	System.err.println("service with bofy and headers!");
    	R response = null;
    	
    	// 记录日志
    	
    	//......
    	
    	// 前置处理
    	beforeService();
    	
    	// 核心处理
    	doService();
    	
    	// 后置处理
    	afterService();
    	
    	// ......
    	
    	return response;
    }
}

SDKCardTradeServiceImpl.java

package com.example.interfaces;
public class SDKCardTradeServiceImpl<T,R> extends AbstractSDKService<T,R> implements SDKRemoteService<T,R> {
    
    // 在这里可以选择性重写父类的before()方法
    @Override
    protected void beforeService() {
    	super.beforeService();
    	System.err.println("SDKCardTradeServiceImpl before!");
    }
    
    @Override
    protected R doService() {
    	// 处理刷卡特定业务逻辑
    	System.err.println("SDKCardTradeServiceImpl doService!");
    	return null;
    }
    
    /**
     * 我同时拥有调用远程方法的能力,所以我实现了SDKRemoteService接口
     */
    @Override
    public R call() {
    	return null;
    }
    
    // 在这里可以选择性重写父类的after()方法
    @Override
    protected void afterService() {
    	super.afterService();
    	System.err.println("SDKCardTradeServiceImpl afterService!");
    }
}

SDKNoCardServceImpl.java

package com.example.interfaces;

/**
 * 无卡交易核心业务处理
 * @author huangjiawei
 *
 */
public class SDKNoCardServceImpl<T,R> extends AbstractSDKService<T,R> implements SDKNoCardService<T,R> {
    
    // 在这里可以选择性重写父类的before()方法
    
    @Override
    protected R doService() {
    	return null;
    }
    
    /**
     * 我同时拥有获取卡号的能力,所以我实现了SDKNoCardService接口
     */
    @Override
    public R getCardNo() {
    	return null;
    }
    
    // 在这里可以选择性重写父类的after()方法
    @Override
    protected void afterService() {
    	super.afterService();
    	System.err.println("SDKNoCardServceImpl 后置处理");
    }
}

Main.java

package com.example.interfaces;

public class Mian {
public static void main(String[] args) {
    SDKCardTradeServiceImpl<String,String> o = new SDKCardTradeServiceImpl<>();
    o.service(null);
    System.err.println("======================================");
    
    SDKNoCardServceImpl<String, String> p = new SDKNoCardServceImpl<>();
    p.service(null);
    }
}

程序输出:

service with only request!
service with body and request!
service with bofy and headers!
SDKCardTradeServiceImpl before!
SDKCardTradeServiceImpl doService!
SDKCardTradeServiceImpl afterService!
======================================
service with only request!
service with body and request!
service with bofy and headers!
SDKNoCardServceImpl 后置处理

注意我们实际调用的时候只需要调用顶层的service()即可,面向接口编程。

二、总结

认真看,其实这个已经定义好了一个大模板了,会非常实用的。小编认为,一般的设计思路是先采用一个顶层接口进行高层抽象,然后创建其他接口扩展顶层接口,在顶层接口下建立一层抽象层,提供默认的实现,定义模板方法给不同的业务类重写。当然,处理本文的做法,一般处理web请求我们也可以通过AOP切面的方式对前置处理和后置处理进行拦截,即利用环绕通知即可,但是,采用aop的缺点可能会获取不到request请求的参数(没测过,大佬们说会,我也不清楚为什么,等你来告诉小编!),本文的方法利用了java面向对象的特性,扩展性会更好点,不过略抽象! 本文讲解的比较抽象,很多地方可能没有讲的特别清楚,同学们可以评论,我们一起探讨探讨。