sprigboot中过滤器执行顺序源码解读

前言

    本文主要是搞清楚对于同一请求在springboot项目中自定义的filter和jar包中的filter的执行顺序是如何指定的。

1.请求在到达自定义controller中方法之前都是进过了哪些过滤器?

    首先从请求出发,考虑请求在到达自定义controller之前都是进过了哪些过滤器。
    直接进入主题,所有的请求访问时都会经过一个个过滤器,方法返回的时候会按照过滤器的顺序从后面往前之前执行,专业术语叫过滤器链,就好比,你从一楼爬到五楼,一定是从五楼顺着走回一楼,请求也是如此。请求发送过来,涉及到过滤器执行的重要方法是ApplicationFilterChain.java中internalDoFilter,里面定义了过滤器的执行顺序

private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
// pos表示当前请求已通过过滤器链的个数;n表示过滤器链的总个数,this.filters中包含了请求需要通过的过滤器数组,ApplicationFilterConfig 可以理解为过滤器对应的配置类。
    if (this.pos < this.n) {
        ApplicationFilterConfig filterConfig = this.filters[this.pos++];

        try {
            Filter filter = filterConfig.getFilter();
            if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
                request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
            }

            if (Globals.IS_SECURITY_ENABLED) {
                Principal principal = ((HttpServletRequest)request).getUserPrincipal();
                Object[] args = new Object[]{request, response, this};
                SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
            } else {
            // 执行过滤器中自己实现的doFilter逻辑
                filter.doFilter(request, response, this);
            }

        }
        // 省略部分无关代码
        }
    } else {
        try {
           		// 省略部分无关代码
           		// 执行severlet具体服务实现逻辑
                this.servlet.service(request, response);
           		// 省略部分无关代码
        } finally {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set((Object)null);
                lastServicedResponse.set((Object)null);
            }

        }

    }
}

    所以从上面可以看到请求发送过来,是按照ApplicationFilterChain.java中filters数组顺序进行执行。下面看下里面的filters数组在项目初始化时如何进行初始化。

2.项目启动过程中如何设置过滤器加载顺序

    项目启动过程中:ServletContextInitializerBeans.java中getOrderedBeansOfType用来获取指定类型的bean集合。其中对filter类型的bean集合处理中就对过滤器的执行顺序进行了设置。

private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type,
			Set<?> excludes) {
			// 获取所有interface javax.servlet.Filter类型的bean名称.
		String[] names = beanFactory.getBeanNamesForType(type, true, false);
		Map<String, T> map = new LinkedHashMap<>();
		for (String name : names) {
			if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {
				T bean = beanFactory.getBean(name, type);
				if (!excludes.contains(bean)) {
					map.put(name, bean);
				}
			}
		}
		List<Entry<String, T>> beans = new ArrayList<>(map.entrySet());
		// 将所有的filter相关的bean按照order比较大小排序,order数值越小的越往前排.
		beans.sort((o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()));
		return beans;
	}

以当前项目为例,项目启动未经过排序处理过滤器顺序:
在这里插入图片描述
排序之后的顺序:
在这里插入图片描述
下面继续深挖beans.sort中的过滤器排序逻辑

beans.sort((o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()));

执行的主要逻辑其实就是获取每个过滤器的order值,然后分别进行比较。逻辑如下:

private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
    	// 省略部分代码
            int i1 = this.getOrder(o1, sourceProvider);
            int i2 = this.getOrder(o2, sourceProvider);
            return Integer.compare(i1, i2);
    }

    获取order值方法为:OrderComparator.java中getOrder,其中通过findOrder获取具体的order值,如果没有则返回默认的order值:Ordered.LOWEST_PRECEDENCE,即为最后执行。

protected int getOrder(@Nullable Object obj) {
		if (obj != null) {
			
			Integer order = findOrder(obj);
			if (order != null) {
				return order;
			}
		}
		// 如果过滤器中没有注解或是成员变量中没有order属性,会给一个默认值,此默认值表示是最后执行
		return Ordered.LOWEST_PRECEDENCE;
	}

    OrderComparator.java中findOrder有两种实现方式,一个是OrderComparator.java中findOrder,实际上就是获取filter中自定义成员变量order值。

protected Integer findOrder(Object obj) {
		return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
	}

比如说OrderedRequestContextFilter.java中,就是通过成员变量定义order值.

public class OrderedRequestContextFilter extends RequestContextFilter implements OrderedFilter {
    private int order = -105;

    public OrderedRequestContextFilter() {
    }

    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}

    另一种方式是AnnotationAwareOrderComparator.java中findOrderFromAnnotation,方法主要的执行逻辑方法名已经说的很清楚:从@Order中获取order值,此处不再展开讲解。比如说自定义过滤器就是通过@Order注解进行指定order值。

@Component
@Slf4j
@Order(Integer.MIN_VALUE)
public class RequestDetailFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("RequestDetailFilter init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
     // 省略业务实现
    }

    @Override
    public void destroy() {
        log.info("RequestDetailFilter destroy");
    }
}

    总结:项目启动过程中,加载bean到容器中,会将filter类型 bean对象按照order值大小进行排序。方法执行时按照已经排好序的filter进行执行。
    到此请求中过滤器执行顺序剖析已梳理完毕,如果感觉有收获欢迎评论区留言或点赞!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
【资说明】 1、基于springboot的在线考试系统项目码.zip 2、该资包括项目的全部码,下载可以直接使用! 3、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 4、本资作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip 基于springboot的在线考试系统项目码.zip
【资说明】 1、该资包括项目的全部码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip 基于springboot的医院门诊管理系统码.zip

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

卖柴火的小伙子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值