责任链模式以及对应框架源码使用场景

定义

责任链模式:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
小编这里理解的,主要目的就是解耦合。

需求

这里小编先讲自己做项目实战过程中用到该模式的场景,目前在做的是电商平台,电商平台中所涉及的模块,主要为商品,用户,营销,订单等等。以商品创建过程为例,这边设计方案,是逐步创建,也就是说从商品的基本信息(名字,类型),商品价格,商品属性,商品上下架状态时间,商品限购条件等等,但管理商品的时候可以有删除按钮(这边不知道各位有没有这个功能),则删除的时候需要逐步删除表结构数据,从基本信息表,价格表商品属性表等等删除,如果都做在一个方法中当然也是可以的,但是因为商品是逐步创建的,这样我们可以使用责任链,将其解耦合。将商品相关编号或id传入,逐个进行删除即可。同时营销活动也一样,基本信息,可购买商品,可参与的用户,送的奖品等等,在删除时也可以使用责任链模式。这样代码在使用者面前简洁有效。
当然这里不可能贴出项目代码,并且项目代码用springIoc管理,大家可以想象怎么让链条单例并扔进去(其实很简单),小编这里有另一个需求,即 博客的评论;那么先阐述一下需求:

博客上大家都可以评论,但是不乏有键盘侠或者对博主单方面有意见的用户进行恶意评论等等,处于这种考虑,系统对评论做了一系列的评论检查功能,比方说评论过长(测试同学经常将一篇小说拷贝进来),评论敏感字符,评论的特殊字符(防止sql注入攻击等)等等,这主要做了评论的风控。

  1. 字数限制
  2. 敏感字符的限定
  3. 特殊字符的排除

话不多说先上代码

评论类,只包含评论内容,和一个用户标示

@Data
@AllArgsConstructor
public class Comment {

    private String msg;

    private String user;
}

评论过滤器,包含频率过滤的方法

public interface CommentFilter {
    /**
     * 评论过滤方法
     * @param comment 评论
     */
    void doFilter(Comment comment);
}

三个评论过滤器的实现类,大家不用较真这些过滤方法啊

//长度过滤
public class CommentLengthFilter implements CommentFilter {
    @Override
    public void doFilter(Comment comment) {
        comment.setMsg(comment.getMsg().substring(0,100));
    }
}
//敏感词过滤
public class CommentSensitiveFilter implements CommentFilter {
    @Override
    public void doFilter(Comment comment) {
        comment.setMsg(comment.getMsg().replaceAll("性爱", "**"));
    }
}
//非法字符过滤
public class CommentIllegalFilter implements CommentFilter {
    @Override
    public void doFilter(Comment comment) {

        comment.setMsg(comment.getMsg().replaceAll(">", ""));
    }
}

过滤器链类编写,这里说明一下为什么链路也要实现过滤器,主要在过滤器链可以加其他过滤器链,
比方说A链有B和C两个过滤器,F有D和E过滤器,则A 可以加入F,同样F可以加入A。使得更加灵活可扩展。

public class CommentFilterChain implements CommentFilter{

    private List<CommentFilter> commentFilterList = new ArrayList<>();

    public void addFilter(CommentFilter filter){
        commentFilterList.add(filter);
    }
    @Override
    public void doFilter(Comment comment){
        for (CommentFilter commentFilter : commentFilterList) {
            commentFilter.doFilter(comment);
        }
    }
}

接下来咱们编写测试类,这边过滤器链可以提前组装好,很多源码中也会提前写好过滤链路顺序

public class CommentCheckTest {

    private CommentFilterChain chain;

    @Before
    public void initChainLit() {
        chain = new CommentFilterChain();
        chain.addFilter(new CommentLengthFilter());
        chain.addFilter(new CommentIllegalFilter());
        chain.addFilter(new CommentSensitiveFilter());
    }

    @Test
    public void commentChainTest() {
        Comment comment = new Comment("理性爱戴国产产品>.........省略500字","lin");
        chain.doFilter(comment);
        System.out.println(comment.getMsg());
    }
}

讲到这里一个完整的责任链就出来了,如果平常的话,我们可以将三个方法写到一个方法中去,很多小伙伴觉得没必要啊,全部写入更加直观更容易理解,但是随着需求的不断增多,这个方法也会不多扩大。接着我们加入一个新的需求
** 加入黑名单过滤 ,假如发现黑名单则后续的过滤器不需要再次执行,那目前的过滤器链可以抛异常中断接下来的操作即可,但是不优雅切得捕获异常,当然也可以过滤后返回值来判断,但是仍然不优雅,还得改原有逻辑 **

思考一下,继续上代码。
黑名单过滤器类

public class CommentBlackFilter implements CommentFilter {

    private CommentFilter filter;

    public CommentBlackFilter(CommentFilter filter) {
        this.filter = filter;
    }

    @Override
    public void doFilter(Comment comment) {
        if ("Bob".equals(comment.getUser())) {
            return;
        }
        filter.doFilter(comment);
    }
}

然后测试类修改,这样后续内容就没有过滤了,这边再次提醒小伙伴,测试尽量用断言啊。控制台打印可以自己看看,也可以集成log(题外话)

@Test
    public void commentChainBlackTest() {
        Comment comment = new Comment("理性爱戴国产产品>.........省略500字","Bob");
        CommentBlackFilter commentBlackFilter = new CommentBlackFilter(chain);
        System.out.println(comment.getMsg());
    }

这样在不破坏原有的基础上,咱们扩展了责任链,当然也可以增加额外的逻辑,包括对传入的参数修改等等。

_ 到这里,我们的责任链模式基本讲完了,值得注意的是责任链实现方法有两种,一种为数组形式,一种为引用形式,各位觉得哪一种比较适合好,可以在评论区留言,当然运用得当可以将两种形式互相结合起来使用。接下来是在源码框架中的运用场景,大家如果不关心的话可以直接跳到总结了。_

在源码框架中的使用

Tomcat中的使用:首先大家猜一下tomcat的责任链模式是使用数组的方式还是引用的形式?
首先tomcat 责任链结构图
在这里插入图片描述
这边主要的责任链为ApplicationFilterChain,里面自己实现了一个数组扩容,也可以用ArrayList实现,可能年代比较久远tomcat自己实现了一套,包括过滤器总数,当当前位置小于过滤器总是即(pos<n)时则执行逻辑。

具体实现逻辑扩容

void addFilter(ApplicationFilterConfig filterConfig) {

        // Prevent the same filter being added multiple times
        for(ApplicationFilterConfig filter:filters)
            if(filter==filterConfig)
                return;

        if (n == filters.length) {
            ApplicationFilterConfig[] newFilters =
                new ApplicationFilterConfig[n + INCREMENT];
            System.arraycopy(filters, 0, newFilters, 0, n);
            filters = newFilters;
        }
        filters[n++] = filterConfig;

    }

具体链条实现逻辑

private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                }
                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                        ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
                } else {
                    filter.doFilter(request, response, this); //重要步骤
                }
            } catch (IOException | ServletException | RuntimeException e) {
                throw e;
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                throw new ServletException(sm.getString("filterChain.filter"), e);
            }
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        try {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(request);
                lastServicedResponse.set(response);
            }

            if (request.isAsyncSupported() && !servletSupportsAsync) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                        Boolean.FALSE);
            }
            // Use potentially wrapped request from this point
            if ((request instanceof HttpServletRequest) &&
                    (response instanceof HttpServletResponse) &&
                    Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();
                Object[] args = new Object[]{req, res};
                SecurityUtil.doAsPrivilege("service",
                                           servlet,
                                           classTypeUsedInService,
                                           args,
                                           principal);
            } else {
                servlet.service(request, response); //重要步骤
            }
        } catch (IOException | ServletException | RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.servlet"), e);
        } finally {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(null);
                lastServicedResponse.set(null);
            }
        }
    }

通过以上源代码可以看到其实是使用数组形式实现的。

接下来我们介绍用引用类型实现的框架源码 :MyBatis
二级缓存责任链图
在这里插入图片描述
查看源码在CacheBuild中先进行配置,然后使用put,get方法直接使用缓存即可

public Cache build() {
    setDefaultImplementations();
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    // issue #352, do not apply decorators to custom caches
    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      cache = new LoggingCache(cache);
    }
    return cache;
  }


private Cache setStandardDecorators(Cache cache) {
    try {
      MetaObject metaCache = SystemMetaObject.forObject(cache);
      if (size != null && metaCache.hasSetter("size")) {
        metaCache.setValue("size", size);
      }
      if (clearInterval != null) {
        cache = new ScheduledCache(cache);
        ((ScheduledCache) cache).setClearInterval(clearInterval);
      }
      if (readWrite) {
        cache = new SerializedCache(cache);
      }
      cache = new LoggingCache(cache);
      cache = new SynchronizedCache(cache);
      if (blocking) {
        cache = new BlockingCache(cache);
      }
      return cache;
    } catch (Exception e) {
      throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
    }
  }

其他表现形式

  • 过滤器链 (上面tomcat等已经举例)
  • 拦截器链 (springMVC拦截器即可,小伙伴有兴趣了解一下,小编一直觉得过滤器和拦截器差不多,原来是设计模式一样,拦截器同样可以用过滤器链来实现。差不多,只是职责不一样,业务场景不同,因为拦截器在业务逻辑之前和之后,包括返回结果之前还会过滤,dubbo同时也一样。)
  • 管道流 (netty的pipeline,包括java8的stream流)
  • 经典责任链(钉钉请假流程)
    以上内容大家可以自行阅读,这边不在复述了。

总结

责任链模式是一种对象行为型模式,其主要优点如下。
1、降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
2、增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
3、增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
4、责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
5、责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

其主要缺点如下。
1、不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
2、对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
3、职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

参考和感谢

感谢源码阅读网的鲁班大叔,讲解的责任链模式。
参考网址:http://c.biancheng.net/view/1397.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
事务的传播机制是指在多个数据库操作中,事务的执行方式和影响范围。常见的事务传播机制有以下几种: 1. PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。 2. PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。 3. PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。 4. PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务。 使用场景: 1. PROPAGATION_REQUIRED:适用于大多数业务场景,保证数据的一致性。 2. PROPAGATION_SUPPORTS:适用于只读操作,不需要事务的场景。 3. PROPAGATION_MANDATORY:适用于必须在事务中执行的场景,比如在一个事务中调用另一个需要事务的方法。 4. PROPAGATION_REQUIRES_NEW:适用于需要独立事务的场景,比如在一个事务中调用一个需要回滚的方法。 5. PROPAGATION_NOT_SUPPORTED:适用于不需要事务的场景,比如查询操作。 6. PROPAGATION_NEVER:适用于不允许在事务中执行的场景,比如在一个事务中调用一个不需要事务的方法。 7. PROPAGATION_NESTED:适用于需要嵌套事务的场景,比如在一个事务中调用一个需要回滚的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木兮君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值