前言
本文主要是搞清楚对于同一请求在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进行执行。
到此请求中过滤器执行顺序剖析已梳理完毕,如果感觉有收获欢迎评论区留言或点赞!