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
容器中。
源码分析
首先进入SpringApplication
的run()
方法,可以看到,此时的Servlet
服务器还未启动
往下走,程序跑到了AbstractApplicationContext
的refresh()
方法,此时项目中的所有bean都加载完毕并添加进Ioc
容器中
往下走到ServletWebServerApplicationContext
的onRefresh()
方法,可以看到有一个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
实例对象,而该对象主要是为组件提供一些基础功能例如order
、onStartup()
方法,并且该类实现了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()