emailjava中怎么校验_责任链模式原理和示例及其在Spring源码中的运用

什么是责任链模式

责任链模式(Chain of Responsibility Pattern)是指将链中的每一个节点看作是一个对象,每个节点处理的请求均不同,且每个节点内部自动维护了一个下一个节点对象。当一个请求在链路的头部发出时,会沿着链的路径依次传递给每一个节点对象,直到有对象处理这个请求为止。

责任链模式属于行为型模式。

写法示例

Talk is cheap,Show me the code。我们就以一个登录校验账号密码,角色,权限等信息的功能为例,直接来看一下责任链模式是怎么写的。

登录用户信息类

首先我们创建一个登录用户信息类:

package com.zwx.design.pattern.chainOfResponsibility;public class LoginUser {    private String loginName;    private String password;    private String roleName;    private String permission;    public String getLoginName() {        return loginName;    }    public void setLoginName(String loginName) {        this.loginName = loginName;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public String getRoleName() {        return roleName;    }    public void setRoleName(String roleName) {        this.roleName = roleName;    }    public String getPermission() {        return permission;    }    public void setPermission(String permission) {        this.permission = permission;    }}

Handler抽象类

创建一个Handler抽象类,这个类维护了链路中的下一个对象,并且将真正处理逻辑的方法doHandler只进行了抽象定义,具体留给实现类去实现:

package com.zwx.design.pattern.chainOfResponsibility;public abstract class MyHandler {    protected MyHandler next;    public void next(MyHandler handler){        this.next = handler;    }    public abstract void doHandler(LoginUser loginUser);}

链路节点Handler实现类

接下来就是创建具体的实现类来实现MyHandler类。链中的每个节点只处理一件事,所以这个示例中我们可以拆分分三个节点,一个校验账号密码,一个校验角色,一个校验角色。

校验账号密码Handler

创建一个节点用来校验账号密码:

package com.zwx.design.pattern.chainOfResponsibility;import org.apache.commons.lang3.StringUtils;public class VerifyAccountHandler extends MyHandler {    @Override    public void doHandler(LoginUser loginUser) {        if (StringUtils.isBlank(loginUser.getLoginName())){            System.out.println("用户名不能为空");            return;        }        if (StringUtils.isBlank(loginUser.getPassword())){            System.out.println("密码不能为空");            return;        }        if (!loginUser.getPassword().equals("123456")){            System.out.println("密码不正确");            return;        }        System.out.println("账号密码校验通过");                next.doHandler(loginUser);    }}

注意最后一句话,next.doHandler(loginUser)是用来调用链路中下一个节点的处理方法

校验角色Handler

新增一个校验角色Handler

package com.zwx.design.pattern.chainOfResponsibility;public class VerifyRoleHanlder extends MyHandler {    @Override    public void doHandler(LoginUser loginUser) {        if(!"admin".equals(loginUser.getRoleName())){            System.out.println("角色信息有误");            return;        }        System.out.println("角色信息校验通过");                next.doHandler(loginUser);    }}

同样的,这里也需要调用下一个节点的处理方法

校验权限Handler

新增一个校验权限的Handler:

package com.zwx.design.pattern.chainOfResponsibility;public class VerifyPermissionHanlder extends MyHandler {    @Override    public void doHandler(LoginUser loginUser) {        if (!"admin".equals(loginUser.getPermission())){            System.out.println("暂无权限");            return;        }        System.out.println("权限校验通过,登录成功");    }}

因为permission已经是最后一个节点,所以这里不需要再继续制定下一个节点了,内部也没有再维护下一个节点对象了。

测试运行结果

现在让我们来看一下应该如何调用上面的示例:

package com.zwx.design.pattern.chainOfResponsibility;public class TestChain {    public static void main(String[] args) {        MyHandler accountHandler = new VerifyAccountHandler();        MyHandler roleHanlder = new VerifyRoleHanlder();        MyHandler permissionHanlder = new VerifyPermissionHanlder();        accountHandler.next(roleHanlder);        roleHanlder.next(permissionHanlder);        LoginUser loginUser = new LoginUser();        loginUser.setLoginName("孤狼1号");        loginUser.setPassword("123");        loginUser.setRoleName("admin");        loginUser.setPermission("admin");        accountHandler.doHandler(loginUser);//从起点开始调用    }}

输出结果:

密码不正确

如果将密码修改为正确密码123456,则输出如下结果:

账号密码校验通过角色信息校验通过权限校验通过,登录成功

和传统写法对比

我们先来看下传统的这种登录逻辑的写法:

package com.zwx.design.pattern.chainOfResponsibility;import org.apache.commons.lang3.StringUtils;public class LoginService {    public void login(LoginUser loginUser){        //1.校验账号密码        if (StringUtils.isBlank(loginUser.getLoginName())){            System.out.println("用户名不能为空");            return;        }        if (StringUtils.isBlank(loginUser.getPassword())){            System.out.println("密码不能为空");            return;        }        if (!loginUser.getPassword().equals("123456")){            System.out.println("密码不正确");            return;        }        //2.角色        if(!"admin".equals(loginUser.getRoleName())){            System.out.println("角色信息有误");            return;        }        //3.校验权限        if (!"admin".equals(loginUser.getPermission())){            System.out.println("暂无权限");            return;        }        System.out.println("校验通过,登录成功");    }}

看起来写法上似乎比通过责任链模式写法简单明了,但是一堆业务代码全部堆在一起,而且我们示例中的逻辑校验比较简单,如果逻辑变得很复杂,那么将各种逻辑校验做一个解耦拆分对后期维护是非常有利的。

责任链模式结合建造者模式

上面的示例写法中,最后在调用过程中有点不是很优雅,由此我们联想到了建造者模式的链式写法,接下来让我们结合建造者模式来对其进行改写。

改写Handler抽象类

改写时我们只需要对顶层抽象类进行改写:

package com.zwx.design.pattern.chainOfResponsibility.build;import com.zwx.design.pattern.chainOfResponsibility.LoginUser;public abstract class BuildHandler {    protected BuildHandler next;    public void next(BuildHandler handler){        this.next = handler;    }    public abstract void doHandler(LoginUser loginUser);    public static class Builder{        private BuildHandler head;        private BuildHandler tail;        public Builder addHanlder(BuildHandler handler){            if (null == head){//head==null表示第一次添加到队列                head = this.tail = handler;                return this;            }            this.tail.next(handler);//原tail节点指向新添加进来的节点            this.tail = handler;//新添加进来的节点设置为tail节点            return this;        }        public BuildHandler build(){            return this.head;        }    }}

这个类中,我们通过一个静态内部类Builder来讲链构造成一个队列。

测试运行结果

其他三个类只需要实现改造之后的BuilderHanlder类,其他不需要修改,那么我们来看看现在的测试类又应该如何调用:

package com.zwx.design.pattern.chainOfResponsibility.build;import com.zwx.design.pattern.chainOfResponsibility.*;public class TestBuildChain {    public static void main(String[] args) {        LoginUser loginUser = new LoginUser();        loginUser.setLoginName("孤狼1号");        loginUser.setPassword("123456");        loginUser.setRoleName("admin");        loginUser.setPermission("admin");        BuildHandler.Builder builder = new BuildHandler.Builder();        builder.addHanlder(new VerifyAccountHandler())                .addHanlder(new VerifyRoleHanlder())                .addHanlder(new VerifyPermissionHanlder());        builder.build().doHandler(loginUser);    }}

输出结果:

账号密码校验通过角色信息校验通过权限校验通过,登录成功

可以看到,改写之后在调用时会优雅很多。

责任链模式角色

从上面的示例中,可以明确,责任链模式只有两个角色:

  • 1、抽象处理者(Handler)::定义一个请求处理的方法,并维护一个下一个处理节点的Handler对象
  • 2、具体处理者(ConcreteHandler):对请求就行处理,只处理自己部分,处理完之后可以进行转发

责任链模式适用场景

责任链模式主要是解耦了请求与处理,用户只需要将请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点进行处理。可以适用于如下场景:

  • 1、多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。
  • 2、在不明确指定接收者的情况下,向多个对象中的一个提交请求
  • 3、可以动态指定一组对象的处理请求。

责任链模式源码中体现

责任链模式应用比较广泛的就是拦截器。
我们先一下Servlet中的J2EE规范定义的一个拦截器接口:

8ee08d3de52b0fbc02c05560401b6b98.png


我们发现这脸只有一个doFilter方法,并没有维护一个链里面的下一个对象。那么这个是怎么实现链路传递的呢?
我们看一下Spring的实现MockFilterChain:

b05e004c924abf05ae20c35bc0c08bb4.png
2a99a6440684de9af65213d67bece1b6.png


从上面两段代码可以发现,子类通过一个List来构建“链路”,最终调用的时候就是通过遍历List来实现“链路”传递。

责任链模式优缺点

任何一个设计模式都有优点和缺点,那么责任链模式有何优缺点呢?

优点

  • 1、将请求与处理解耦
  • 2、请求处理者(链路中的节点)只需关注自己感兴趣的请求进行处理,对于不感兴趣或者无法处理的请求直接转发给下一个处理者
  • 3、具备链式传递请求的功能,请求发送者无需知晓链路结构,只需等待请求处理结果
  • 4、链路结构灵活,可以通过改变链路结构动态的新增或者删减责任
  • 5、易于扩展新的请求处理类,符合开闭原则

缺点

  • 1、如果责任链的链路太长或者处理时间过程,会影响性能。
  • 2、如果节点对象存在循环引用时,会造成死循环,导致系统崩溃

总结

本文介绍了责任链模式的基本用法,并通过一个示例将责任链模式和建造者模式结合起来使用,使得代码更加优雅,同时也介绍了Spring中对责任链模式的应用,希望通过本文的学习,大家可以更好地理解责任链模式的原理,可以在合适的场景中进行实际应用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值