设计一个简单可复用的责任链。

298 阅读4分钟

什么是责任

假设有A,B,C,D四个类按照某种顺序排序,它们都是处理一组类似的业务场景(我们项目核销场景),它们根据需求判断这件事该不该自己处理,如果该类处理不了就传递到下一个类,直到找到能够处理这件事情类或没有类能够处理这件事的为止。

具体实现

先看一幅实现类结构图: 实现类结构图 ChainBus为统一去执行该链中的方法的类,ChainHub为统一存储链中的类,ChainRegister为一个注册器,通过该方法将方法注册到ChainHub中。

package cn.gw.common.chain;

import java.io.Serializable;

/**
* @author gaowu
* @date 2020/7/24 19:24
*/
public interface IChain extends Serializable {
}

excute方法的参数bean必须实现的类(往下看会知道)。

package cn.gw.common.chain;
import cn.gw.core.model.Resp;
/**
 * @author gaowu
 * @date 2020/7/24 19:27
 */
public interface IChainHandler<T extends IChain> {

    Integer order();

    Resp execute(T t);

}

如果一个类需要成为一个Chain,则需要实现该方法IChainHandler,Order为类的排序,execute为业务逻辑代码。注:相同一组的链中的方法,如支付,他们的参数是相同的,如支付场景(OrderPayChian),则excute方法参数为OrderPayChain。

package cn.gw.common.chain;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 存储责任链的类
 *
 * @author gaowu
 * @date 2020/7/24 19:23
 */
public class ChainHub {

    private static Map<Class, List<IChainHandler>> chainHandlersMap = new HashMap<>(16);

    public static void register(Class<? extends IChain> clazz, IChainHandler handler) {
        chainHandlersMap.computeIfAbsent(clazz, val -> new ArrayList<>()).add(handler);
    }

    public static List<IChainHandler> getChainHandlersByClass(Class<? extends IChain> clazz) {
        List<IChainHandler> handlers = chainHandlersMap.get(clazz);
        return handlers == null ? new ArrayList<>() : handlers;
    }

    public static Map<Class, List<IChainHandler>> getChainHandlersMap() {
        return chainHandlersMap;
    }

}

ChainHub为统一存储链式方法的类,按照execute中的参数分组,如:参数分别为OneChian与TwoChain的方法则为不同的链,应该分别存储到不同的List中。

package cn.gw.common.chain;

import cn.gw.core.model.Resp;

/**
 * @author gaowu
 * @date 2020/7/24 20:11
 */
public interface IChainBus<T extends IChain> {

    Resp apply(T t);

}

ChainBus的接口,返回类型为Resp(可自定义一个统一的返回类型),传入参数为实现IChian的Bean。

package cn.gw.common.chain;

import cn.gw.core.exception.BaseException;
import cn.gw.core.model.HttpCodeEnum;
import cn.gw.core.model.Resp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 责任链的执行器
 *
 * @author gaowu
 * @date 2020/7/24 19:23
 */
@Component
public class ChainBus implements IChainBus<IChain> {

    private Logger log = LoggerFactory.getLogger(ChainBus.class);

    @Override
    public Resp apply(IChain iChain) {
        List<IChainHandler> handlers = ChainHub.getChainHandlersByClass(iChain.getClass());
        Resp result = null;
        for (IChainHandler handler : handlers) {
            try {
                result = handler.execute(iChain);
            } catch (Exception ex) {
                return buildFailResult(ex);
            }
            // result不为空说明handler,execute执行成功
            if (result != null) {
                break;
            }
        }
        if (result == null || result.getCode() == null) {
            return Resp.fail("无法执行该请求!");
        }
        return result;
    }

    private Resp buildFailResult(Exception ex) {
        log.error(ex.getMessage(), ex);
        HttpCodeEnum code = HttpCodeEnum.INTERNAL_SERVER_ERROR;
        if (ex instanceof BaseException) {
            code = ((BaseException) ex).getCode();
        }
        return Resp.fail(code, ex.getMessage());
    }

}

ChainBus,统一去执行该链中的方法的类,根据参数获取ChainHub存储的该类型的所有的方法,依次执行,若该类有返回值证明该方法执行成功,就不往后执行,若执行到最后方法还是无法执行,则直接抛异常或者返回一个特定的错误值。

package cn.gw.common.chain;

import java.lang.reflect.Method;

/**
 * 责任链注册器
 *
 * @author gaowu
 * @date 2020/7/24 20:20
 */
public class ChainRegister {

    private static final String EXECUTE_METHOD = "execute";

    public static void doRegistration(IChainHandler iChainHandler) {
        Class<? extends IChain> clazz = getIChainClassByHandlerClass(iChainHandler.getClass());
        if (clazz != null) {
            ChainHub.register(clazz, iChainHandler);
        }
    }

    private static Class<? extends IChain> getIChainClassByHandlerClass(Class<?> clazz) {
//        try {
//            Method executeMethod = clazz.getDeclaredMethod(EXECUTE_METHOD, IChain.class, IChainBus.class);
//            return getExecuteMethodParamsType(executeMethod);
//        } catch (NoSuchMethodException e) {
//            return null;
//        }
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (EXECUTE_METHOD.equals(method.getName()) && !method.isBridge()) {
                return getExecuteMethodParamsType(method);
            }
        }
        return null;
    }

    private static Class<? extends IChain> getExecuteMethodParamsType(Method method) {
        Class<?>[] exeParams = method.getParameterTypes();
        if (exeParams.length == 0) {
            return null;
        }
        for (Class<?> clazz : exeParams) {
            if (IChain.class.isAssignableFrom(clazz)) {
                return (Class<? extends IChain>) clazz;
            }
        }
        return null;
    }

}

ChainRegister责任链的注册器,通过该类的doRegistration方法,将实现了IChainHandler接口方法,获取execute方法参数的class,存储到ChainHub中的chainHandlersMap中。

package cn.gw.app.config;

import cn.gw.common.chain.ChainRegister;
import cn.gw.common.chain.IChainHandler;
import cn.gw.common.helper.ApplicationContextHelper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * @author gaowu
 * @date 2020/5/1 2:40
 */
@Configuration
public class BootStrap {

    @Bean
    public void initChain() {
        List<IChainHandler> iChainHandlers = new ArrayList<>(ApplicationContextHelper.getBeansOfType(IChainHandler.class).values());
        iChainHandlers.sort(Comparator.comparing(IChainHandler::order));
        iChainHandlers.forEach(iChainHandler -> {
            ChainRegister.doRegistration(iChainHandler);
        });
    }
}

initChain获取Spring容器的IChainHandler,根据他们的Oder排序,依次通过注册器注册。以上就是所有责任链类的实现,下面就是责任链具体的使用例子。

package cn.gw.app.chain.newc;

import cn.gw.common.chain.IChain;
import lombok.Data;

/**
 * @author gaowu
 * @date 2020/7/24 20:52
 */
@Data
public class OneChain implements IChain {

    private String name;
}

package cn.gw.app.chain.newc;

import cn.gw.common.chain.IChainHandler;
import cn.gw.core.exception.ServiceException;
import cn.gw.core.model.Resp;
import org.springframework.stereotype.Component;

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

/**
 * @author gaowu
 * @date 2020/7/24 20:52
 */
@Component
public class OneFistChainHandler implements IChainHandler<OneChain> {

    @Override
    public Integer order() {
        return 0;
    }

    @Override
    public Resp execute(OneChain oneChain) {
        String name = oneChain.getName();
        if ("aaa".equals(name)) {
            System.out.println("OneFistChainHandler -> " + name );
            if (true){
                throw new ServiceException("bad request");
            }
            Map<String, String> reslut = new HashMap<>(2);
            reslut.put("name", name);
            return Resp.ok(reslut);
        }
        return null;
    }
}

package cn.gw.app.chain.newc;

import cn.gw.common.chain.IChainHandler;
import cn.gw.core.model.Resp;
import org.springframework.stereotype.Component;

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

/**
 * @author gaowu
 * @date 2020/7/24 20:57
 */
@Component
public class OneSecondChainHandler implements IChainHandler<OneChain> {

    @Override
    public Integer order() {
        return 10;
    }

    @Override
    public Resp execute(OneChain oneChain) {
        String name = oneChain.getName();
        if ("bbb".equals(name)) {
            System.out.println("OneSecondChainHandler -> " + name);
            Map<String, String> reslut = new HashMap<>(2);
            reslut.put("name", name);
            reslut.put("name++", name);
            return Resp.ok(reslut);
        }

        return null;
    }

}

上面为一组参数为OneChain的责任链。

    @IgnoreAuth
    @PostMapping(value = "/newChain")
    public Resp tetNewChain(@RequestBody OneChain chain) {
        return iChainBus.apply(chain);
    }

测试责任链是否正常执行。 正常数据测试。 测试execute方法有异常的情况是否能捕捉成功。 测试该链中没有类执行该业务场景,是否正常。以上测试都能说明该链的基本设计没毛病,如果更好的建议欢迎留言。