Spring如何将Bean(Filter、Interceptor等)注册到Servlet服务器

Spring管理组件并注册到Servlet

Spring与Servlet的关系

在使用Spring项目的时候突然意识到一个问题,Spring主要的只是提供了一个管理Bean的功能,没有让bean直接在servlet容器生效的功能。

比如,对一个Filter声明为Bean,如何生效

首先我们要知道,过滤器是Servlet提供的

在这里插入图片描述

以SpringBoot为例,它与Servlet的工作关系可以理解为

Spring Boot 项目是一个独立的 Java 应用程序,它内置了一个 Servlet 容器(比如 Tomcat、Jetty 等),并提供了一个嵌入式的 Web 服务器。当你运行 Spring Boot 项目时,实际上是在嵌入式的 Servlet 容器中启动了一个 Spring 应用程序。

在 Spring Boot 项目中,通常会有一个入口类(比如带有 @SpringBootApplication 注解的类),这个类中包含了一个 main 方法。当你运行这个 main 方法时,Spring Boot 会自动启动内置的 Servlet 容器,并初始化 Spring 应用程序上下文,加载并管理应用程序中的所有 Bean。

可以看到Spring程序上下文里面有servlet上下文、Ioc容器、环境等一些信息

在这里插入图片描述

然后,Servlet 容器会根据配置加载并初始化应用程序中的 Servlet、Filter、Listener 等组件,并在接收到 HTTP 请求时调用它们,从而处理请求并生成响应。

可以理解为Spring 框架提供了对 Servlet 技术的封装和扩展,使得在使用 Servlet 编程时更加方便和灵活。比如,Spring MVC 就是基于 Servlet API 的一个 Web MVC 框架,它提供了一种基于注解的方式来定义控制器、处理请求,并结合了 Spring 的依赖注入和面向切面编程等特性,使得开发 Web 应用更加简单和灵活。

既然如此,想让自定义Filter生效,就需要将Filter通过Servlet API注册到Servlet容器中,Spring可以通过@Component或者其他注解声明一个WebFilter并生成实例对象到Ioc容器中,虽然WebFilter类实现了Filter接口,但并没有明确地将其注册到Servlet容器中。~~然而,由于Spring容器实例化了这个类,并且该类实现了Filter接口,所以它仍然可以被识别为一个过滤器。~~所以Spring需要做的便是把Filter的bean注册到Servlet中去。

自定义Filter

@Component
class WebVisitFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        // 业务处理
        System.out.println("过滤起生效了");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

如此定义一个Filter,便会被Spring声明为Bean并添加到Ioc容器进行管理,在服务启动过程中便会被添加到Servlet容器中。

源码分析

首先进入SpringApplicationrun()方法,可以看到,此时的Servlet服务器还未启动

在这里插入图片描述

往下走,程序跑到了AbstractApplicationContextrefresh()方法,此时项目中的所有bean都加载完毕并添加进Ioc容器中
在这里插入图片描述

往下走到ServletWebServerApplicationContextonRefresh()方法,可以看到有一个createWebServer()方法,结合类的注释可以知道,这是个Servlet Web 服务器的应用上下文,可以创建Servlet服务器和注册Filter等组件。

在这里插入图片描述

然后进入createWebServer()方法,断点处便是获取Servlet服务器的逻辑,getSelfInitializer() 方法返回了一个 ServletContextInitializer 实例,而这个实例是通过一个 lambda 表达式 this::selfInitialize 创建的。lambda 表达式 this::selfInitialize 实际上是一个函数引用,它引用了当前对象(this)的 selfInitialize 方法。

在这里插入图片描述

getWebServer()主要是对Tomcat服务器进行一些属性配置,最后会调用getTomcatWebServer()方法,而后通过initialize()对服务器进行启动
在这里插入图片描述

这里往后走会进行一个准备阶段(在此省略),通过准备阶段之后便会进行组件的注册,通过ServletContextInitializer 实例参数,将组件注册到服务
在这里插入图片描述

这里需要注意的是两个方法,getServletContextInitializerBeans()onStartup()

  • getServletContextInitializerBeans() : 获取所有需要在内嵌 Web 服务器中注册的 ServletContextInitializer 实例并返回,以便在启动时将它们注册到 Web 服务器中。
    在这里插入图片描述

  • onStartup便是将组件注册到Web服务器的操作

进入getServletContextInitializerBeans(),里面会调用下面这个构造方法并返回示例,而this.initializers便是ServletContextInitializer集合
在这里插入图片描述

进入addAsRegistrationBean()方法,这里主要是根据
在这里插入图片描述

进入createRegistrationBean,可以看到使自定义的Filter
在这里插入图片描述

这是在将Filter创建为RegistrationBean实例对象,而该对象主要是为组件提供一些基础功能例如orderonStartup()方法,并且该类实现了ServletContextInitializer,意味着这便是我们要返回的结果
在这里插入图片描述

获取ServletContextInitializer集合之后,遍历执行onStartup方法

在这里插入图片描述
在这里插入图片描述

然后进入register,挨个添加进servletContext
在这里插入图片描述
在这里插入图片描述

剩下的configure便是对组件属性的一些配置(跳过),回到TomcatWebServer,可以看到Tomcat服务器里面已经注册了五个过滤器,包括自定义的webVisitFilter
在这里插入图片描述

回到SpringApplication,可以看到webServer已经存在
在这里插入图片描述

但可以看到,webServer里面并没有Filter的相关属性定义,因为组件是被注册到servlet上下文中的

在这里插入图片描述

至此,自定义过滤器便生效了,如果把WebVisitFilter@Component去掉,则过滤器失效,因为Ioc中不存在过滤器的Bean,所以无法注册ServletContext中。

调用链

SpringApplication.run() – > AbstractApplicationContext.refresh() --> ServletWebServerApplicationContext.refresh() --> ServletWebServerApplicationContext.createWebServer() --> TomcatWebServer.initialize() --> Tomcat.start() – > ServletWebServerApplicationContext.selfInitialize() – >ServletWebServerApplicationContext.getServletContextInitializerBeans() --> ServletContextInitializerBeans.addAdaptableBeans() -->ServletContextInitializerBeans. addAsRegistrationBean() --> ServletContextInitializerBeans.FilterRegistrationBeanAdapter.createRegistrationBean() --> FilterRegistrationBean.FilterRegistrationBean() --> AbstractFilterRegistrationBean.addRegistration()

  • 27
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值