FilterChainProxy分析

FilterChainProxy 探索分析

今天看一个项目中的前端请求接口响应时间,controller方法监控只有2-300ms,但是页面上浏览器返回却占用了将近3s。后来用jprofile工具追踪了一下调用路径,发现方法在到达controller 方法之前执行了两个过滤器的方法,占用了一定的时间。借机分析了一下过滤器的执行过程。

FilterChainProxy 与Spring Security 过滤链相关,具体可查看相关源码

可参考 :

Spring Security 过滤链一 - 概念与设计

spring security自动配置的源码简单分析

Spring Security 源码分析

详细可查看这些源码,分析 FilterChainProxy 的具体使用及功能。

本篇内容重点介绍FilterChainProxy 过滤时使用的责任链模式。

doFilterInternal 方法

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
       throws IOException, ServletException {
    boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
    if (!clearContext) {
       doFilterInternal(request, response, chain);
       return;
    }
    try {
       request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
       // 重点是这个方法 
       doFilterInternal(request, response, chain);
    }
    catch (Exception ex) {
       Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
       Throwable requestRejectedException = this.throwableAnalyzer
             .getFirstThrowableOfType(RequestRejectedException.class, causeChain);
       if (!(requestRejectedException instanceof RequestRejectedException)) {
          throw ex;
       }
       this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response,
             (RequestRejectedException) requestRejectedException);
    }
    finally {
       SecurityContextHolder.clearContext();
       request.removeAttribute(FILTER_APPLIED);
    }
}

具体实现:

private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
       throws IOException, ServletException {
    FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
    HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
    // 查看 getFilters逻辑
    List<Filter> filters = getFilters(firewallRequest);
    if (filters == null || filters.size() == 0) {
       if (logger.isTraceEnabled()) {
          logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
       }
       firewallRequest.reset();
       chain.doFilter(firewallRequest, firewallResponse);
       return;
    }
    if (logger.isDebugEnabled()) {
       logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
    }
    // 创建虚拟的过滤器链
    VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
    // 执行过滤器
    virtualFilterChain.doFilter(firewallRequest, firewallResponse);
}

getFilters:获取所有的过滤器

private List<Filter> getFilters(HttpServletRequest request) {
    int count = 0;
    for (SecurityFilterChain chain : this.filterChains) {
       if (logger.isTraceEnabled()) {
          logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)", chain, ++count,
                this.filterChains.size()));
       }
        // 从 DefaultSecurityFilterChain 中,拿到注册好的Filters
       if (chain.matches(request)) {
          return chain.getFilters();
       }
    }
    return null;
}

virtualFilterChain.doFilter:执行过滤器

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
    if (this.currentPosition == this.size) {
       if (logger.isDebugEnabled()) {
          logger.debug(LogMessage.of(() -> "Secured " + requestLine(this.firewalledRequest)));
       }
       // Deactivate path stripping as we exit the security filter chain
       this.firewalledRequest.reset();
       this.originalChain.doFilter(request, response);
       return;
    }
    this.currentPosition++;
    Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
    if (logger.isTraceEnabled()) {
       logger.trace(LogMessage.format("Invoking %s (%d/%d)", nextFilter.getClass().getSimpleName(),
             this.currentPosition, this.size));
    }
    nextFilter.doFilter(request, response, this);
}

点睛之笔:nextFilter.doFilter(request, response, this);

这里才是过滤链调度的地方。

public interface Filter {

    default void init(FilterConfig filterConfig) throws ServletException {}

    void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    default void destroy() {}
}

每个过滤器肯定实现了 Filter接口,其核心就是 doFilter方法。所以当执行到 virtualFilterChain.doFilter时,传入了一个this,那么当前过滤器执行完成以后,又可以回到当前的逻辑中执行下一个Filter。

执行顺序在调用getFilters() 方法的时候就已经拿到了,源码中又可以修改执行顺序的地方,这里不详细描述。

重点总结

参考 virtualFilterChain.doFilter,责任链可以这么用:

重点理解责任链一个设计的逻辑:

1、肯定要有个调度者

2、调度者有一个方法能拿到所有的处理器

3、定义一个接口和方法,所有的处理器实现这个方法,方法中包含一个入参就是调度者

4、调度者中拿到处理器以后,调用这个方法,调用的时候把自身传进去。

具体可根据业务的实际情况设计

原来的设计模式我记得是每一个处理器里面注入下一个处理器,感觉还是有耦合性,不如这个设计好,记录一下以后实践

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值