一、概述
工作中比较常见的业务场景,比如对外提供一个数据上传接口,基本的业务逻辑,一般都有参数非空校验、安全校验(比如IP、非正常请求等)、黑白名单校验、规则拦截等等。很多开发伙伴会使用异常来实现。
public class Upload{
public void checkNullParam(Object param){
//参数非空校验
throw new RuntimeException();
}
public void checkSecurity(){
//安全校验
throw new RuntimeException();
}
public void checkBackList(){
//黑名单校验
throw new RuntimeException();
}
public void checkRule(){
//规则拦截
throw new RuntimeException();
}
}
二、业务校验的设计
上面的示例代码使用了异常来做逻辑条件判断,如果后续逻辑越来越复杂的话,会出现一些问题:如异常只能返回异常信息,不能返回更多的字段,这时候需要自定义异常类。而且异常的处理效率比条件判断方式要低很多。
阿里开发手册规定:禁止用异常做逻辑判断 ,这里可以考虑责任链模式。
责任链模式:将处理不同逻辑的对象连接成一个链表结构,每个对象都保存下一个节点的引用:
- 一个接口或者抽象类
- 每个对象差异化处理
- 对象链(数组)初始化(连起来)
2.1 一个接口或者抽象类
- 有一个指向责任下一个对象的属性
- 一个设置下一个对象的set方法
- 给子类对象差异化实现的方法(如代码中的doFilter方法)
public abstract class AbstractHandler {
//责任链中的下一个对象
private AbstractHandler nextHandler;
/**
* 责任链的下一个对象
*/
public void setNextHandler(AbstractHandler nextHandler){
this.nextHandler = nextHandler;
}
public AbstractHandler getNextHandler() {
return nextHandler;
}
/**
* 具体参数拦截逻辑,给子类去实现
*/
public void filter(Request request, Response response) {
doFilter(request, response);
if (getNextHandler() != null) {
getNextHandler().filter(request, response);
}
}
/**
* 子类对象差异化实现的方法
*/
abstract void doFilter(Request filterRequest, Response response);
}
2.2 每个对象差异化处理
/**
* 参数校验对象
**/
@Component
@Order(1) //顺序排第1,最先校验
public class CheckParamFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
System.out.println("非空参数检查");
}
}
/**
* 黑名单校验对象
*/
@Component
@Order(2) //校验顺序排第2
public class CheckBlackFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
//校验黑名单
System.out.println("校验黑名单");
}
}
/**
* 规则拦截对象
*/
@Component
@Order(3) //校验顺序排第3
public class CheckRuleFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
//check rule
System.out.println("check rule");
}
}
2.3 对象链初始化(责任链连起来)&& 使用
@Component("ChainPatternDemo")
public class ChainPatternDemo {
//自动注入各个责任链的对象
@Autowired
private List<AbstractHandler> abstractHandleList;
private AbstractHandler abstractHandler;
//spring注入后自动执行,责任链的对象连接起来
@PostConstruct
public void initializeChainFilter(){
for(int i = 0;i<abstractHandleList.size();i++){
if(i == 0){
//责任链的第一个节点
abstractHandler = abstractHandleList.get(0);
}else{
//责任链的对象连接起来
AbstractHandler currentHander = abstractHandleList.get(i - 1);
AbstractHandler nextHander = abstractHandleList.get(i);
currentHander.setNextHandler(nextHander);
}
}
}
//使用---直接调用exec
public Response exec(Request request, Response response) {
abstractHandler.filter(request, response);
return response;
}
public AbstractHandler getAbstractHandler() {
return abstractHandler;
}
public void setAbstractHandler(AbstractHandler abstractHandler) {
this.abstractHandler = abstractHandler;
}
}
三、责任链总结
优点:
- 1 将请求与处理解耦。
- 2 请求处理者将不是自己职责范围内的请求,转发给下一个节点。
- 3 请求发送者不需要关系链路结构,只需等待请求处理结果即可。
- 4 链路结构灵活,易于扩展新的请求处理节点
缺点:
- 责任链太长或者处理时间过长,会影响整体性能。
- 如果节点对象存在循环引用,则会造成死循环,导致程序崩溃。
我目前的项目中,业务校验是把doFilter() 返回值优化成 Boolean类型,然后在filter() 方法中判断为true才继续往下调用,这样可以提升整体的性能。