spring-boot-2.4.0中添加servlet filter的执行顺序问题
1、先看源码中类ServletContextInitializerBeans初始化的过程
  • 构造方法
	public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes) {
		... //省略非相关代码
		addServletContextInitializerBeans(beanFactory);
		addAdaptableBeans(beanFactory);
		List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
				.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
				.collect(Collectors.toList());
		this.sortedList = Collections.unmodifiableList(sortedInitializers);
		logMappings(this.initializers);
	}
  • 查看构造方法中调用的方法addAdaptableBeans(beanFactory);
	protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
		MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
		addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
		addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
		...... //省略非相关代码
	}

可以看到实现Filter.class的bean注入主要是通过addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());这段代码实现的

  • 继续查看方法addAsRegistrationBean
	protected <T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
			RegistrationBeanAdapter<T> adapter) {
		addAsRegistrationBean(beanFactory, type, type, adapter);
	}
	
	private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
			Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
		List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
        ...... //省略非相关代码
	}
  • 继续查看方法getOrderedBeansOfType,此方法实现了filter的排序功能
	private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type,
			Set<?> excludes) {
		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());
		beans.sort((o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()));  //实现filter的排序功能
		return beans;
	}
	
  • 继续查看AnnotationAwareOrderComparatorcompare方法
	public int compare(@Nullable Object o1, @Nullable Object o2) {
		return doCompare(o1, o2, null);
	}

	private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
	    //判断是否实现优先排序接口,若其中一个实现另一个未实现则可直接返回优先级
		boolean p1 = (o1 instanceof PriorityOrdered);
		boolean p2 = (o2 instanceof PriorityOrdered);
		if (p1 && !p2) {
			return -1;
		}
		else if (p2 && !p1) {
			return 1;
		}

        //均实现优先排序接口或均未实现排序接口
		int i1 = getOrder(o1, sourceProvider);
		int i2 = getOrder(o2, sourceProvider);
		return Integer.compare(i1, i2);
	}

大致的意思就是从BeanFactory中获取到filter对应的bean,使用AnnotationAwareOrderComparator类进行比较排序,接下来再看看排序的细节。

2、Ordered接口与@Order注解的解读

从源码中可以看出使用AnnotationAwareOrderComparatorfilter的排序是通过对实现了Ordered接口、@Order注解的解析出order值完成排序,如果两个都定义了的话优先取实现接口Ordered的排序值,示例如下:

@Order(20)
class DemoOrder implements Ordered{

    @Override
    public int getOrder() {
        return 10;
    }
    
}

DemoOrder中既实现了Ordered接口也定义了@Order注解,其结果是取10,实现接口的优先判断返回,查看findOrder源码可以看到如果能从实现接口中获取到排序就已return返回了

  • AnnotationAwareOrderComparatorfindOrder
	protected Integer findOrder(Object obj) {
		Integer order = super.findOrder(obj);
		if (order != null) {
			return order;
		}
		return findOrderFromAnnotation(obj);
	}
3、自定义Filter的两种方式
  • 3.1、使用@Component注册为普通的RegistrationBean
@Order(10)
@Component
public class DemoFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
    }

}
  • 3.2、使用@Webfilter注解以及@ServletComponentScan注解
@WebFilter
public class DemoWebFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
    }

}

//需同时在启动类中加入@ServletComponentScan

需注意,在使用@Webfilter注解时,若使用了@Order或继承了Ordered接口均无法改变Filter的顺序的,只能改变类的命名调整Filter的顺序

  • 3.3、分析@Webfilter增加@Order注解或实现Ordered接口无效原因

我们回看类ServletContextInitializerBeans的构造方法中的addServletContextInitializerBeans(beanFactory);可以跟踪到若使用@Webfilter注册的bean,其order值相同均为默认优先级最低值order=2147483647,是由AnnotationAwareOrderComparator类的findOrder方法获取,那为什么不会获取到类中定义的实现Ordered的接口呢?接着分析一下类的注册过程

  • 3.4、addAdaptableBeans(beanFactory);方法获取FilterRegistrationBean是通过ServletContextInitializerBeans的方法addAsRegistrationBean,如下
     	private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
              Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
                  // 省略非关键代码
                  RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
                  int order = getOrder(bean);
                  registration.setOrder(order); //获取到order值重新设置
                  this.initializers.add(type, registration);
                  // 省略非关键代码
              }
          }
      }
    
  • 3.5、addServletContextInitializerBeans(beanFactory);方法获取FilterRegistrationBean是通过AbstractBeanFactory类的方法,如下
	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		        // 省略非关键代码
				try {
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				// 省略非关键代码
		}
	}

可以看到两种方式的不同,addAsRegistrationBean重新设置了order排序

如果使用了Ordered接口或者@Order注解,则按优先级排序,如果没有则排到最后,如果有多个没有Order的接口,则按类名的字典排序。


赞赏(Donation)
微信(Wechat Pay)

donation-wechatpay