责任链模式与其在Spring MVC中的应用

简单用例

   现有一开发场景,要求设计一个集团采购物品,不同金额交由不同领导审批的流程

   现有三级领导,金额在5000以下的,由部门领导审批;金额在10000以下的由院校级领导审批;金额在10000以上的,由校级领导审批。虽然使用 if-else 也能实现改功能,但是代码的可读性、可扩展性差,使用责任链设计模式,是面向对象的思维方式,符合“开闭原则”,具体实现的代码如下:
责任链调用发起者

 public static void main(String[] args) {
        PurchaseRequest purchaseRequest = new PurchaseRequest(1, 5001, 11);

        DepartmentApprover departmentApprover = new DepartmentApprover("部门审批者 张xx");
        CollegeApprover collegeApprover = new CollegeApprover("院系审批者 李xx");
        SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("校长审批 王xx");

        //设置责任链的下一个处理类(该段设置chain的逻辑,也可以在不同的approver子类中设置,但缺点是不易管理)
        departmentApprover.nextApprover=collegeApprover;
        collegeApprover.nextApprover=schoolMasterApprover;
        //为实现调用chain中任意一个处理类,都可以得到正确的结果,在这里构建一个环形链表
        schoolMasterApprover.nextApprover=departmentApprover;

        schoolMasterApprover.processRequest(purchaseRequest);//输出:请求编号:11 当前审批人名:院系审批者 李xx
    }

批准人抽象类

public abstract class Approver {
    Approver nextApprover;//下一个处理者
    String name;

    public Approver(String name) {
        this.name = name;
    }
    //交由子类去完善处理审批的逻辑
    public abstract void processRequest(PurchaseRequest purchaseRequest);
}

部门审批者、院系审批者、校长审批实现类

public class DepartmentApprover extends Approver{

    public DepartmentApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if(purchaseRequest.price<=5000f){
            System.out.println("请求编号:" + purchaseRequest.id + " 当前审批人名:" + this.name);
        }else {
            nextApprover.processRequest(purchaseRequest);
        }
    }
}


public class CollegeApprover extends Approver {
    public CollegeApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.price > 5000f && purchaseRequest.price <= 10000) {
            System.out.println("请求编号:" + purchaseRequest.id + " 当前审批人名:" + this.name);
        } else {
            nextApprover.processRequest(purchaseRequest);
        }
    }
}


public class SchoolMasterApprover extends Approver {
    public SchoolMasterApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if(purchaseRequest.price>10000f){
            System.out.println("请求编号:" + purchaseRequest.id + " 当前审批人名:" + this.name);
        }else {
            nextApprover.processRequest(purchaseRequest);
        }
    }
}

使用责任链的注意事项和细节

  • 降低了流程中,调用链链中某一节点执行逻辑的耦合度,提高了系统的灵活性
  • 当链中节点比较多时,系统的性能会受到影响,因此,可以考虑控制最长链节点。

设计模式是一种思想,在不同的地方,实现的方式可能不同,但大致的思想都是一致的,都是为了提高扩展性,可读性等原因。


实际生产用例

场景

  • 一个档案文件可能会经历移交、归档、长期保持环节,在经历不同的环节时需要执行四性检测
  • 在对一个档案文件的四性检测中,需要对真实、完整、可靠、安全性进行检测,且在四性中包含了103个检测项目。
  • 四性检测对每一个文件都需要过,检测项目按具体需求进行检测。

实现

整体实现思路:
  将四性的类通过执行链串起来,103个检测项目方法在对应的四性里,期间的执行结果通过 DetectionBuilder 类来保存。对于检测项目,通过反射去调用。

//detectionBuilder 中包含本次检测需要的多个信息
DetectionBuilder detectionBuilder = new DetectionBuilder( lists, dataInfo.getId(), arcfile, DetectionSegmentType.移交,
                adjunctFileInfos, fourDetectionRecordService.getLastDetection( dataInfo.getId(), DetectionSegmentType.移交 ) );
//调用入口。
Detection detection = FourDetectionFactory.getHandler().detectionChain( detectionBuilder ).getDetection();

FourDetectionFactory 类,获取、设置链顺序:

public class FourDetectionFactory {
    /**
     * 一个handler头
     */
    private static AbstractDetectionHandler allHandler;
    public static HashMap<String, String> codeAndMethodName;

    //初始化detection_code与项目方法名的对应
    static {
        codeAndMethodName = new HashMap((int) (103 / 0.75 + 1));

        codeAndMethodName.put("GD-1-1", "doFixityInformationDetection");
		……
        codeAndMethodName.put("GD-4-7", "doSysProcessSafety");

        codeAndMethodName.put("YJ-1-1", "doFixityInformationDetection");
    	……
        codeAndMethodName.put("YJ-4-7", "doSysProcessSafety");

        codeAndMethodName.put("BC-1-1", "doFixityInformationDetection");
       	……
        codeAndMethodName.put("BC-4-9", "doSaveEnvironment");
    }

    public static synchronized AbstractDetectionHandler getHandler() {
        if (allHandler != null) {
            return allHandler;
        }
        // 通过接口从IOC容器中获取对应的实现类
        DetectionService realDetectionService = SpringUtils.getBean(RealDetectionService.class);
        DetectionService completeDetectionService = SpringUtils.getBean(CompleteDetectionService.class);
        DetectionService availableDetectionService = SpringUtils.getBean(AvailableDetectionService.class);
        DetectionService safeDetectionService = SpringUtils.getBean(SafeDetectionService.class);
        if (realDetectionService == null) {
            throw new IllegalArgumentException("error: realDetectionService bean 注册失败!");
        }
        if (completeDetectionService == null) {
            throw new IllegalArgumentException("error: completeDetectionService bean 注册失败!");
        }
        if (availableDetectionService == null) {
            throw new IllegalArgumentException("error: availableDetectionService bean 注册失败!");
        }
        if (safeDetectionService == null) {
            throw new IllegalArgumentException("error: safeDetectionService bean 注册失败!");
        }
        allHandler = new AbstractDetectionHandler(null);
        AbstractDetectionHandler realHandler = new AbstractDetectionHandler(realDetectionService);
        AbstractDetectionHandler completeHandler = new AbstractDetectionHandler(completeDetectionService);
        AbstractDetectionHandler availableHandler = new AbstractDetectionHandler(availableDetectionService);
        AbstractDetectionHandler safeHandler = new AbstractDetectionHandler(safeDetectionService);
        allHandler.setNextHandler(realHandler);
        realHandler.setNextHandler(completeHandler);
        completeHandler.setNextHandler(availableHandler);
        availableHandler.setNextHandler(safeHandler);
        return allHandler;
    }
}

AbstractDetectionHandler 具体链的调用方法,其中 detectionService 在不同的 AbstractDetectionHandler 实例中赋予了不同的值:

public class AbstractDetectionHandler {
     // 责任链中的下一个元素
    protected AbstractDetectionHandler nextHandler;
    // detectionService是链中四个对象的父接口
    protected DetectionService detectionService;

    public void setNextHandler(AbstractDetectionHandler handler) {
        this.nextHandler = handler;
    }
   // 四性检测开始执行,使用责任链模式将任务串联起来,递归调用
    public DetectionBuilder detectionChain(DetectionBuilder detectionBuilder) {
        try {
            detectionBuilder = doDetection(detectionBuilder);
            if (nextHandler != null) {
                detectionBuilder = nextHandler.detectionChain(detectionBuilder);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return detectionBuilder;
    }
    //当前detectionBuilder需要执行的检测环节
    private DetectionBuilder doDetection(DetectionBuilder detectionBuilder) {
        DetectionSegmentType code = detectionBuilder.getDetection().getCode();
        if (code == null) {
            throw new IllegalArgumentException("参数缺失");
        }
        if (this.detectionService != null) {
            switch (code) {
                case 归档:
                    detectionBuilder = detectionService.doPostDetection(detectionBuilder);
                    break;
                case 移交:
                    detectionBuilder = detectionService.doMoveDetection(detectionBuilder);
                    break;
                case 长期保存:
                    detectionBuilder = detectionService.doSaveDetection(detectionBuilder);
                    break;
            }

        }
        return detectionBuilder;
    }
    public AbstractDetectionHandler(DetectionService detectionService) {
        this.detectionService = detectionService;
    }
}

AvailableDetectionServiceImpl 是 detectionService 的一个子类,在 AbstractDetectionHandler 类的 doDetection() 中调用,下面截取了类中的主要逻辑方法。

  • doPostDetection() 方法在 detectionService 的其他三个子类中都有实现,因为三个环节中都需要执行对应的四性检测。
  • doNeedDetection() 方法是为了实现按需执行检测项目(103个检测项不一定全部执行)。由于该方法使用的是反射调用类中的方法(即检测项目),故使用注解注入需要的实现类(涉及到 IOC 中循环注入可参考博主该文章)。
@Service
public class AvailableDetectionServiceImpl implements implements AvailableDetectionService {
	@Resource
    private RealDetectionService realDetectionService;
    @Resource
    private CompleteDetectionService completeDetectionService;
    @Resource
    private AvailableDetectionService availableDetectionService;
    @Resource
    private SafeDetectionService safeDetectionService;
    
    /**
     * 归档 可用性检测
     */
    @Override
    public DetectionBuilder doPostDetection(DetectionBuilder detectionBuilder) {
        doNeedDetection(fourDetection,detectionBuilder);
        return detectionBuilder;
    }
 	/**
     * 在环节和四性下检测需要的检测项目
     */
    public void doNeedDetection(String fourDetection, DetectionBuilder detectionBuilder) {
        //按拿到要检测的项目;反射调用对应的方法
        //通过文件的档案库id拿到检查项目,如果没有该级,则向上递归一级
        String orgId = detectionBuilder.getDetection().getArcfile().getOwnOrgId();
        String generalId = detectionBuilder.getDetection().getArcfile().getGeneralId();
        String repositoryId = detectionBuilder.getDetection().getArcfile().getRepositoryId();
        List<String> orgGeneralRepository = new ArrayList<>();
        if(StringUtils.isNotBlank(repositoryId)){
            orgGeneralRepository.add(repositoryId);
        }else if(StringUtils.isNotBlank(generalId)){
            orgGeneralRepository.add(generalId);
        }else {
            orgGeneralRepository.add(orgId);
        }

        DetectionSegmentType nowLinkType = detectionBuilder.getDetection().getCode();
        //将拿到该文件当前环节、当前四性下的检测项目
        List<FourDetection> fourDetectionList = fourDetectionServiceImpl.getFourDetectionByGeneralId(orgGeneralRepository, nowLinkType.getValue(), fourDetection);

        //遍历调用方法
        fourDetectionList.forEach(e -> {
            //判断该项目是否属于当前的环节
            if (e == null || !nowLinkType.getValue().equals(e.getLinkType())) {
                return;
            }
            DetectionService service = null;
            //自动注入的都将为成员变量在反射后,都将为null,故需要修改为从ioc中获取对应的对象
            DetectionClass detectionClass = (DetectionClass) EnumService.getEnum(DetectionClass.class, e.getFourDetection());
            switch (detectionClass) {
                case 真实性:
                    service = realDetectionService;
                    break;
                case 完整性:
                    service = completeDetectionService;
                    break;
                case 可用性:
                    service = availableDetectionService;
                    break;
                case 安全性:
                    service = safeDetectionService;
                    break;
            }
            if (service == null) {
                return;
            }
            Class<? extends DetectionService> aClass = service.getClass();
            try {
                String methodName = FourDetectionFactory.codeAndMethodName.get(e.getDetectionCode());//是否包含改方法的判断doElectronicDocumentsDetection
                Method test = aClass.getDeclaredMethod(methodName, DetectionBuilder.class);
                test.invoke(service, detectionBuilder);
            } catch (Exception a) {
                a.printStackTrace();
            }
        });
    }
}

责任链在 Spring MVC 的 HandlerExecutionChain 中的应用源码解析

在 Spring MVC 的 DispatcherServlet 类中,有如下的主要代码逻辑:
  doDispatch方法中,构建了 interceptor 执行链,将请求分配给 preHandle 执行,当 preHandle 方法返回为 false时,会调用到 afterCompletion 执行,最后调用 postHandle 方法。

protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception {
            HandlerExecutionChain mappedHandler = null;
            mappedHandler = getHandler(processedRequest);
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
           //当上面的 applyPreHandle 返回false时,继续向下执行到
            mappedHandler.applyPostHandle(processedRequest, response, mv);
}


//调用拦截器的 interceptor.preHandle
boolean applyPreHandle (HttpServletRequest request, HttpServletResponse response) {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;*}
    }
    return true;
}
void triggerAfterCompletion (HttpServletRequest request, HttpServletResponse response,Exception ex){
     HandlerInterceptor interceptor = interceptors[i];
     try {
         interceptor.afterCompletion(request, response, this.handler, ex);
     }
 }
        
//调用拦截器的 interceptor.postHandle
void applyPostHandle (HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv){
    HandlerInterceptor interceptor = interceptors[i];
    interceptor.postHandle(request, response, this.handler, mv);
}
  • HandlerExecutionChain 本身不会执行 interceptor 的逻辑,只是将请求分配给 chain 上注册的 interceptor 执行;
  • HandlerExecutionChain 维护了 HandlerInterceptor 的集合。

监听器(listener)、过滤器(filter)、拦截器(interceptor)的执行顺序参考
SpingMvc的整体执行流程参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值