1.我们自定义一个过滤器,比如用来计算接口的执行时长。
@WebFilter
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long start = System.currentTimeMillis();
chain.doFilter(request, response);
long end = System.currentTimeMillis();
System.out.println("接口执行时间: " + (end - start));
}
}
2.自定义一个Controller层代码:(里面是什么逻辑不重要)
@RestController
public class MyController {
@PostMapping("/hello")
public Student hello(@Validated @RequestBody Student student) {
return student;
}
}
3.启动类:主要需要加上@ServletComponentScan注解:
@SpringBootApplication
@ServletComponentScan
public class Main8080 {
public static void main(String[] args) {
SpringApplication.run(Main8080.class,args);
}
}
4.此时访问一下接口,结果如下:可见这个过滤器是正常运行的。

5.那么此时,如果我代码中出现某个业务类,需要引用到这个过滤器怎么办?例如:
@Service
public class AdminService {
@Autowired
private MyFilter myFilter;
}
此时程序重新启动就会报错:

首先我们应该了解一下,@ServletComponentScan注解是用来干啥的,简单点说就是:
Servlet 可以直接通过 @WebServlet 注解自动注册。Filter可以直接通过 @WebFilter 注解自动注册。Listener可以直接通过 @WebListener 注解自动注册。首先,我们从上文的案例结果可以看出,此时我们自定义的MyFilter类,并不会被加载到SpringBoot的容器中。那是不是@WebFilter这个注解有什么特殊的地方呢?
首先我们看下它的出处:
@Service:import org.springframework.stereotype.Service;@WebFilter:import javax.servlet.annotation.WebFilter;可见, @WebFilter 他并不是Spring本身自带的一种注解。 那么我们再来看下,SpringBoot在启动的过程中,是否对这个注解做出了一定的处理。我们全局搜索import javax.servlet.annotation.WebFilter;,可以看到以下结果:

可以看到WebFilterHandler这个类中对这个注解做了对应的处理,那好了,我们在里面打个断点瞅瞅:

根据左下角调用栈,我们重点关注ServletComponentRegisteringPostProcessor这个处理器,看下源码发现,这个类里面有一个静态代码块和一段扫描代码:
class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
private static final List<ServletComponentHandler> HANDLERS;
static {
List<ServletComponentHandler> servletComponentHandlers = new ArrayList<>();
servletComponentHandlers.add(new WebServletHandler());
servletComponentHandlers.add(new WebFilterHandler());
servletComponentHandlers.add(new WebListenerHandler());
HANDLERS = Collections.unmodifiableList(servletComponentHandlers);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (isRunningInEmbeddedWebServer()) {
ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();
for (String packageToScan : this.packagesToScan) {
scanPackage(componentProvider, packageToScan);
}
}
}
private void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, String packageToScan) {
for (BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)) {
if (candidate instanceof AnnotatedBeanDefinition) {
for (ServletComponentHandler handler : HANDLERS) {
handler.handle(((AnnotatedBeanDefinition) candidate),
(BeanDefinitionRegistry) this.applicationContext);
}
}
}
}
}
从这个静态代码块,我们可以发现跟上文中讲到的@ServletComponentScan注解的作用是吻合的。这两者是有关联的,在下文会说。
上述代码结合本文案例来说就是:
WebFilterHandler类会处理所有被@WebFilter注解的类。registry.registerBeanDefinition(name, builder.getBeanDefinition());这段代码进行注册。那我们执行一下这段代码,看看会怎么样。步骤如下:
让代码跑到这一步:

对这行代码,alt+左键(即查看执行结果,虽然这个函数并不会有返回,但是查看的同时相当于执行了一遍这段代码),然后下一步就能发现抛出了异常:

异常的完整信息是:
org.springframework.beans.factory.support.BeanDefinitionOverrideException:
Invalid bean definition with name ‘com.filter.MyFilter’ defined in null:
Cannot register bean definition [Root bean: clas s[org.springframework.boot.web.servlet.FilterRegistrationBean];
这里的意思,通俗点就是,无法注册一个FilterRegistrationBean类型的Bean。也就是说,我们执行这段代码,想要注册我们通过@WebFilter注解修饰的过滤器的时候,这个过滤器的类型是FilterRegistrationBean。
封装的这一个环节,可想而知是发生在类的实例化阶段。我们可以在自定义的过滤器中加一个构造函数,然后打个断点,debug下瞅瞅:
public MyFilter() {
System.out.println("MyFilter");
}
调试如下:

我们可以发现,从tomcat启动开始,就会调用onStartup()函数进行一些初始化的启动操作。根据调用链,我们可以定位到selfInitialize()这个函数中,看下代码:

我们可以得知,这个时候,FilterRegistrationBean这个类型的Bean需要被初始化。我们可以看下getServletContextInitializerBeans()这个函数上的注释:

那么紧接着往后看,看到createBean()这个函数:这个函数到目前为止应该是用于创建FilterRegistrationBean的实例的,只不过这里装配了下MyFilter自身。

我们知道,Spring容器中的Bean,其加载过程分为三大类:详细的可以看Spring源码系列:Bean的加载。
Bean实例的创建。从调用栈看来,这三个步骤确实都包含了:

并且,调用栈的意思应该是:
创建了FilterRegistrationBean的实例。
对FilterRegistrationBean的属性进行注入,此时需要创建MyFilter类型的Bean。

对MyFilter的Bean重复做三个动作:实例化、属性注入、初始化:

但是最终装配的对象是是一种InnerBean。
对于本文案例的MyFilter类而言,它满足两个条件:
@WebFilter修饰。@Autowired注解来自动装配了进来。然后我们再来说说InnerBean的情况。首先,我们从上面的调试流程来看,我们知道,我们项目中的MyFilter类的类型是一种InnerBean。也就是内部bean。因为内部bean总是匿名的并且总是由外部bean创建的。因此不可能单独访问内部bean,也不可能将它们注入到协作bean中。
结合本文案例来说就是:
MyFilter 对象是一个InnerBean。无法通过@Autowired private MyFilter myFilter;的方式去引入。@WebFilter相关的过滤器而言,真正注册到Spring容器中的是FilterRegistrationBean类。我们可以对需要引入过滤器的业务类这么更改:
更改前
@Service
public class AdminService {
@Autowired
private MyFilter myFilter;
}
更改后
@Service
public class AdminService {
@Autowired
@Qualifier("com.filter.MyFilter")
private FilterRegistrationBean myFilter;
}