通过上节课的两个案例,我们了解了容器运行时过滤器的工作原理,那么这节课我们还是通过两个错误案例,来学习下容器启动时过滤器初始化以及排序注册等相关逻辑。了解了它们,你会对如何使用好过滤器更有信心。下面,我们具体来看一下。

案例 1:@WebFilter 过滤器使用 @Order 无效

假设我们还是基于 Spring Boot 去开发上节课的学籍管理系统,这里我们简单复习下上节课用到的代码。

首先,创建启动程序的代码如下:

@SpringBootApplication
@ServletComponentScan
@Slf4j
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        log.info("启动成功");
    }
}

实现的 Controller 代码如下:

@Controller
@Slf4j
public class StudentController {
    @PostMapping("/regStudent/{name)}")
    @ResponseBody
    public String saveUser(String name) throws Exception {
        System.out.println("......用户注册成功");
        return "success";
    }
}

上述代码提供了一个 Restful 接口 "/regStudent"。该接口只有一个参数 name,注册成功会返回"success"。

现在,我们来实现两个新的过滤器,代码如下:

AuthFilter:例如,限制特定 IP 地址段(例如校园网内)的用户方可注册为新用户,当然这里我们仅仅 Sleep 1 秒来模拟这个过程。

@WebFilter
@Slf4j
@Order(2)
public class AuthFilter implements Filter {
    @SneakyThrows
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        if(isPassAuth()){
            System.out.println("通过授权");
            chain.doFilter(request, response);
        }else{
            System.out.println("未通过授权");
            ((HttpServletResponse)response).sendError(401);
        }
    }
    private boolean isPassAuth() throws InterruptedException {
        System.out.println("执行检查权限");
        Thread.sleep(1000);
        return true;
    }
}

TimeCostFilter:计算注册学生的执行耗时,需要包括授权过程。

@WebFilter
@Slf4j
@Order(1)
public class TimeCostFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("#开始计算接口耗时");
        long start = System.currentTimeMillis();
        chain.doFilter(request, response);
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("#执行时间(ms):" + time);
    }
}

在上述代码中,我们使用了 @Order,期望 TimeCostFilter 先被执行,因为 TimeCostFilter 设计的初衷是统计这个接口的性能,所以是需要统计 AuthFilter 执行的授权过程的。

全部代码实现完毕,执行结果如下:

执行检查权限
通过授权
#开始计算接口耗时
......用户注册成功
#执行时间(ms):33

从结果来看,执行时间并不包含授权过程,所以这并不符合我们的预期,毕竟我们是加了 @Order 的。但是如果我们交换 Order 指定的值,你会发现也不见效果,为什么会如此?难道 Order 不能用来排序 WebFilter 么?下面我们来具体解析下这个问题及其背后的原理。

案例解析

通过上节课的学习,我们得知:当一个请求来临时,会执行到 StandardWrapperValve 的 invoke(),这个方法会创建 ApplicationFilterChain,并通过 ApplicationFilterChain#doFilter() 触发过滤器执行,并最终执行到内部私有方法 internalDoFilter(), 我们可以尝试在 internalDoFilter() 中寻找一些启示:

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();

从上述代码我们得知:过滤器的执行顺序是由类成员变量 Filters 决定的,而 Filters 变量则是 createFilterChain() 在容器启动时顺序遍历 StandardContext 中的成员变量 FilterMaps 获得的:

public static ApplicationFilterChain createFilterChain(ServletRequest request,
        Wrapper wrapper, Servlet servlet) {
    // 省略非关键代码
    // Acquire the filter mappings for this Context
    StandardContext context = (StandardContext) wrapper.getParent();
    FilterMap filterMaps[] = context.findFilterMaps();
    // 省略非关键代码
    // Add the relevant path-mapped filters to this filter chain
    for (int i = 0; i < filterMaps.length; i++) {
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
            continue;
        }
        if (!matchFiltersURL(filterMaps[i], requestPath))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
            context.findFilterConfig(filterMaps[i].getFilterName());
        if (filterConfig == null) {
            continue;
        }
        filterChain.addFilter(filterConfig);
    }
    // 省略非关键代码
    // Return the completed filter chain
    return filterChain;
}

下面继续查找对 StandardContext 成员变量 FilterMaps 的写入引用,我们找到了 addFilterMapBefore():

public void addFilterMapBefore(FilterMap filterMap) {
    validateFilterMap(filterMap);
    // Add this filter mapping to our registered set
    filterMaps.addBefore(filterMap);
    fireContainerEvent("addFilterMap", filterMap);
}

到这,我们已经知道过滤器的执行顺序是由 StandardContext 类成员变量 FilterMaps 的顺序决定,而 FilterMaps 则是一个包装过的数组,所以我们只要进一步弄清楚 FilterMaps 中各元素的排列顺序即可。

我们继续在 addFilterMapBefore() 中加入断点,尝试从调用栈中找到一些线索:

addFilterMapBefore:2992, StandardContext
addMappingForUrlPatterns:107, ApplicationFilterRegistration
configure:229, AbstractFilterRegistrationBean
configure:44, AbstractFilterRegistrationBean
register:113, DynamicRegistrationBean
onStartup:53, RegistrationBean
selfInitialize:228, ServletWebServerApplicationContext
// 省略非关键代码

可知,Spring 从 selfInitialize() 一直依次调用到 addFilterMapBefore(),稍微分析下 selfInitialize(),我们可以了解到,这里是通过调用 getServletContextInitializerBeans(),获取所有的 ServletContextInitializer 类型的 Bean,并调用该 Bean 的 onStartup(),从而一步步以调用栈显示的顺序,最终调用到 addFilterMapBefore()。

private void selfInitialize(ServletContext servletContext) throws ServletException {
   prepareWebApplicationContext(servletContext);
   registerApplicationScope(servletContext);
   WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
   for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
      beans.onStartup(servletContext);
   }
}

那么上述的 selfInitialize() 又从何处调用过来呢?这里你可以先想想,我会在思考题中给你做进一步解释。

现在我们继续查看 selfInitialize() 的细节。

首先,查看上述代码中的 getServletContextInitializerBeans(),因为此方法返回的 ServletContextInitializer 类型的 Bean 集合顺序决定了 addFilterMapBefore() 调用的顺序,从而决定了 FilterMaps 内元素的顺序,最终决定了过滤器的执行顺序。

getServletContextInitializerBeans() 的实现非常简单,只是返回了 ServletContextInitializerBeans 类的一个实例,参考代码如下:

protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
   return new ServletContextInitializerBeans(getBeanFactory());
}

上述方法的返回值是个 Collection,可见 ServletContextInitializerBeans 类是一个集合类,它继承了 AbstractCollection 抽象类。也因为如此,上述 selfInitialize() 才可以遍历 ServletContextInitializerBeans 的实例对象。

既然 ServletContextInitializerBeans 是集合类,那么我们就可以先查看其 iterator(),看看它遍历的是什么。

@Override
public Iterator<ServletContextInitializer> iterator() {
   return this.sortedList.iterator();
}

此集合类对外暴露的集合遍历元素为 sortedList 成员变量,也就是说,上述 selfInitialize() 最终遍历的即为 sortedList 成员变量。

到这,我们可以进一步确定下结论:selfInitialize() 中是通过 getServletContextInitializerBeans() 获取到的 ServletContextInitializer 类型的 Beans 集合,即为 ServletContextInitializerBeans 的类型成员变量 sortedList。反过来说,sortedList 中的过滤器 Bean 元素顺序,决定了最终过滤器的执行顺序

现在我们继续查看 ServletContextInitializerBeans 的构造方法如下:

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
      Class<? extends ServletContextInitializer>... initializerTypes) {
   this.initializers = new LinkedMultiValueMap<>();
   this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
         : Collections.singletonList(ServletContextInitializer.class);
   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);
}

通过第 8 行,可以得知:我们关心的类成员变量 this.sortedList,其元素顺序是由类成员变量 this.initializers 的 values 通过比较器 AnnotationAwareOrderComparator 进行排序的。

继续查看 AnnotationAwareOrderComparator 比较器,忽略比较器调用的细节过程,其最终是通过两种方式获取比较器需要的 order 值,来决定 sortedInitializers 的排列顺序:

  • 待排序的对象元素自身实现了 Order 接口,则直接通过 getOrder() 获取 order 值;

  • 否则执行 OrderUtils.findOrder() 获取该对象类 @Order 的属性。

这里多解释一句,因为 this.initializers 的 values 类型为 ServletContextInitializer,其实现了 Ordered 接口,所以这里的比较器显然是使用了 getOrder() 获取比较器所需的 order 值,对应的类成员变量即为 order。

继续查看 this.initializers 中的元素在何处被添加,我们最终得知,addServletContextInitializerBeans() 以及 addAdaptableBeans() 这两个方法均构建了 ServletContextInitializer 子类的实例,并添加到了 this.initializers 成员变量中。在这里,我们只研究 addServletContextInitializerBeans,毕竟我们使用的添加过滤器方式(使用 @WebFilter 标记)最终只会通过这个方法生效。

在这个方法中,Spring 通过 getOrderedBeansOfType() 实例化了所有 ServletContextInitializer 的子类:

private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
   for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
      for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
            initializerType)) {
         addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
      }
   }
}

根据其不同类型,调用 addServletContextInitializerBean(),我们可以看出 ServletContextInitializer 的子类包括了 ServletRegistrationBean、FilterRegistrationBean 以及 ServletListenerRegistrationBean,正好对应了 Servlet 的三大要素。

而这里我们只需要关心对应于 Filter 的 FilterRegistrationBean,显然,FilterRegistrationBean 是 ServletContextInitializer 的子类(实现了 Ordered 接口),同样由成员变量 order 的值决定其执行的优先级。

private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
      ListableBeanFactory beanFactory) {
   if (initializer instanceof ServletRegistrationBean) {
      Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
      addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
   }
   else if (initializer instanceof FilterRegistrationBean) {
      Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
      addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
   }
   else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
      String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
      addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
   }
   else if (initializer instanceof ServletListenerRegistrationBean) {
      EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
      addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
   }
   else {
      addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
            initializer);
   }
}

最终添加到 this.initializers 成员变量中:

private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
      ListableBeanFactory beanFactory, Object source) {
   this.initializers.add(type, initializer);
// 省略非关键代码
}

通过上述代码,我们再次看到了 FilterRegistrationBean。但问题来了,我们没有定义 FilterRegistrationBean,那么这里的 FilterRegistrationBean 是在哪里被定义的呢?其 order 类成员变量是否有特定的取值逻辑?

不妨回想下上节课的案例 1,它是在 WebFilterHandler 类的 doHandle() 动态构建了 FilterRegistrationBean 的 BeanDefinition:

class WebFilterHandler extends ServletComponentHandler {
   WebFilterHandler() {
      super(WebFilter.class);
   }
   @Override
   public void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,
         BeanDefinitionRegistry registry) {
      BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterRegistrationBean.class);
      builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));
      builder.addPropertyValue("dispatcherTypes", extractDispatcherTypes(attributes));
      builder.addPropertyValue("filter", beanDefinition);
      builder.addPropertyValue("initParameters", extractInitParameters(attributes));
      String name = determineName(attributes, beanDefinition);
      builder.addPropertyValue("name", name);
      builder.addPropertyValue("servletNames", attributes.get("servletNames"));
      builder.addPropertyValue("urlPatterns", extractUrlPatterns(attributes));
      registry.registerBeanDefinition(name, builder.getBeanDefinition());
   }
   // 省略非关键代码

这里我再次贴出了 WebFilterHandler 中 doHandle() 的逻辑(即通过 BeanDefinitionBuilder 动态构建了 FilterRegistrationBean 类型的 BeanDefinition)。然而遗憾的是,此处并没有设置 order 的值,更没有根据 @Order 指定的值去设置。

到这里我们终于看清楚了问题的本质,所有被 @WebFilter 注解的类,最终都会在此处被包装为 FilterRegistrationBean 类的 BeanDefinition。虽然 FilterRegistrationBean 也拥有 Ordered 接口,但此处却并没有填充值,因为这里所有的属性都是从 @WebFilter 对应的属性获取的,而 @WebFilter 本身没有指定可以辅助排序的属性。

现在我们来总结下,过滤器的执行顺序是由下面这个串联决定的:

RegistrationBean 中 order 属性的值 ->

ServletContextInitializerBeans 类成员变量 sortedList 中元素的顺序 ->

ServletWebServerApplicationContext 中 selfInitialize() 遍历 FilterRegistrationBean 的顺序 ->

addFilterMapBefore() 调用的顺序 ->

filterMaps 内元素的顺序 ->

过滤器的执行顺序

可见,RegistrationBean 中 order 属性的值最终可以决定过滤器的执行顺序。但是可惜的是:当使用 @WebFilter 时,构建的 FilterRegistrationBean 并没有依据 @Order 的值去设置 order 属性,所以 @Order 失效了。

问题修正

现在,我们理清了 Spring 启动 Web 服务之前的一些必要类的初始化流程,同时也弄清楚了 @Order 和 @WebFilter 同时使用失效的原因,但这个问题想要解决却并非那么简单。

这里我先提供给你一个常见的做法,即实现自己的 FilterRegistrationBean 来配置添加过滤器,不再使用 @WebFilter。具体代码如下:

@Configuration
public class FilterConfiguration {
    @Bean
    public FilterRegistrationBean authFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new AuthFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(2);
        return registration;
    }
    @Bean
    public FilterRegistrationBean timeCostFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new TimeCostFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(1);
        return registration;
    }
}

按照我们查看的源码中的逻辑,虽然 WebFilterHandler 中 doHandle() 构建了 FilterRegistrationBean 类型的 BeanDefinition,但没有设置 order 的值

所以在这里,我们直接手工实例化了 FilterRegistrationBean 实例,而且设置了其 setOrder()。同时不要忘记去掉 AuthFilter 和 TimeCostFilter 类中的 @WebFilter,这样问题就得以解决了。

案例 2:过滤器被多次执行

我们继续沿用上面的案例代码,要解决排序问题,可能有人就想了是不是有其他的解决方案呢?比如我们能否在两个过滤器中增加 @Component,从而让 @Order 生效呢?代码如下。

AuthFilter:

@WebFilter
@Slf4j
@Order(2)
@Component
public class AuthFilter implements Filter {
    @SneakyThrows
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
        if(isPassAuth()){
            System.out.println("通过授权");
            chain.doFilter(request, response);
        }else{
            System.out.println("未通过授权");
            ((HttpServletResponse)response).sendError(401);
        }
    }
    private boolean isPassAuth() throws InterruptedException {
        System.out.println("执行检查权限");
        Thread.sleep(1000);
        return true;
    }
}

TimeCostFilter 类如下:

@WebFilter
@Slf4j
@Order(1)
@Component
public class TimeCostFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("#开始计算接口耗时");
        long start = System.currentTimeMillis();
        chain.doFilter(request, response);
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("#执行时间(ms):" + time);
    }
}

最终执行结果如下:

#开始计算接口耗时
执行检查权限
通过授权
执行检查权限
通过授权
#开始计算接口耗时
......用户注册成功
#执行时间(ms):73
#执行时间(ms):2075

更改 AuthFilter 类中的 Order 值为 0,继续测试,得到结果如下:

执行检查权限
通过授权
#开始计算接口耗时
执行检查权限
通过授权
#开始计算接口耗时
......用户注册成功
#执行时间(ms):96
#执行时间(ms):1100

显然,通过 Order 的值,我们已经可以随意调整 Filter 的执行顺序,但是我们会惊奇地发现,过滤器本身被执行了 2 次,这明显不符合我们的预期!那么如何理解这个现象呢?

案例解析

从案例 1 中我们已经得知被 @WebFilter 的过滤器,会在 WebServletHandler 类中被重新包装为 FilterRegistrationBean 类的 BeanDefinition,而并非是 Filter 类型。

而当我们在自定义过滤器中增加 @Component 时,我们可以大胆猜测下:理论上 Spring 会根据当前类再次包装一个新的过滤器,因而 doFIlter() 被执行两次。因此看似奇怪的测试结果,也在情理之中了。

我们继续从源码中寻找真相,继续查阅 ServletContextInitializerBeans 的构造方法如下:

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
      Class<? extends ServletContextInitializer>... initializerTypes) {
   this.initializers = new LinkedMultiValueMap<>();
   this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
         : Collections.singletonList(ServletContextInitializer.class);
   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);
}

上一个案例中,我们关注了 addServletContextInitializerBeans(),了解了它的作用是实例化并注册了所有 FilterRegistrationBean 类型的过滤器(严格说,是实例化并注册了所有的 ServletRegistrationBean、FilterRegistrationBean 以及 ServletListenerRegistrationBean,但这里我们只关注 FilterRegistrationBean)。

而第 7 行的 addAdaptableBeans(),其作用则是实例化所有实现 Filter 接口的类(严格说,是实例化并注册了所有实现 Servlet、Filter 以及 EventListener 接口的类),然后再逐一包装为 FilterRegistrationBean。

之所以 Spring 能够直接实例化 FilterRegistrationBean 类型的过滤器,这是因为:

  • WebFilterHandler 相关类通过扫描 @WebFilter,动态构建了 FilterRegistrationBean 类型的 BeanDefinition,并注册到 Spring;

  • 或者我们自己使用 @Bean 来显式实例化 FilterRegistrationBean 并注册到 Spring,如案例 1 中的解决方案。

但 Filter 类型的过滤器如何才能被 Spring 直接实例化呢?相信你已经有答案了:任何通过 @Component 修饰的的类,都可以自动注册到 Spring,且能被 Spring 直接实例化。

现在我们直接查看 addAdaptableBeans(),其调用了 addAsRegistrationBean(),其 beanType 为 Filter.class:

protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
   // 省略非关键代码
   addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
   // 省略非关键代码
}

继续查看最终调用到的方法 addAsRegistrationBean():

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);
   for (Entry<String, B> entry : entries) {
      String beanName = entry.getKey();
      B bean = entry.getValue();
      if (this.seen.add(bean)) {
         // One that we haven't already seen
         RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
         int order = getOrder(bean);
         registration.setOrder(order);
         this.initializers.add(type, registration);
         if (logger.isTraceEnabled()) {
            logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
                  + order + ", resource=" + getResourceDescription(beanName, beanFactory));
         }
      }
   }
}

主要逻辑如下:

  • 通过 getOrderedBeansOfType() 创建了所有 Filter 子类的实例,即所有实现 Filter 接口且被 @Component 修饰的类;

  • 依次遍历这些 Filter 类实例,并通过 RegistrationBeanAdapter 将这些类包装为 RegistrationBean;

  • 获取 Filter 类实例的 Order 值,并设置到包装类 RegistrationBean 中;

  • 将 RegistrationBean 添加到 this.initializers。

到这,我们了解到,当过滤器同时被 @WebFilter 和 @Component 修饰时,会导致两个 FilterRegistrationBean 实例的产生。addServletContextInitializerBeans() 和 addAdaptableBeans() 最终都会创建 FilterRegistrationBean 的实例,但不同的是:

  • @WebFilter 会让 addServletContextInitializerBeans() 实例化,并注册所有动态生成的 FilterRegistrationBean 类型的过滤器;

  • @Component 会让 addAdaptableBeans() 实例化所有实现 Filter 接口的类,然后再逐一包装为 FilterRegistrationBean 类型的过滤器。

问题修正

解决这个问题提及的顺序问题,自然可以继续参考案例 1 的问题修正部分。另外我们也可以去掉 @WebFilter 保留 @Component 的方式进行修改,修改后的 Filter 示例如下:

//@WebFilter
@Slf4j
@Order(1)
@Component
public class TimeCostFilter implements Filter {
   //省略非关键代码
}

重点回顾

这节课我们分析了过滤器在 Spring 框架中注册、包装以及实例化的整个流程,最后我们再次回顾下重点。

@WebFilter 和 @Component 的相同点是:

  • 它们最终都被包装并实例化成为了 FilterRegistrationBean;

  • 它们最终都是在 ServletContextInitializerBeans 的构造器中开始被实例化。

@WebFilter 和 @Component 的不同点是:

  • 被 @WebFilter 修饰的过滤器会被提前在 BeanFactoryPostProcessors 扩展点包装成 FilterRegistrationBean 类型的 BeanDefinition,然后在 ServletContextInitializerBeans.addServletContextInitializerBeans() 进行实例化;而使用 @Component 修饰的过滤器类,是在 ServletContextInitializerBeans.addAdaptableBeans() 中被实例化成 Filter 类型后,再包装为 RegistrationBean 类型。

  • 被 @WebFilter 修饰的过滤器不会注入 Order 属性,但被 @Component 修饰的过滤器会在 ServletContextInitializerBeans.addAdaptableBeans() 中注入 Order 属性。

思考题

这节课的两个案例,它们都是在 Tomcat 容器启动时发生的,但你了解 Spring 是如何整合 Tomcat,使其在启动时注册这些过滤器吗?

最后修改日期: 2022年9月7日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。