目录

chen 的个人博客

VX:ZzzChChen
Phone:13403656751
Email:zxydczzs@gmail.com

X

SpringBoot ContextLoaderListener、Servlet

一、问题

SpringBoot v2.3.2 中使用 ContextLoaderListener.getCurrentWebApplicationContext() 获取 WebApplicationContext 为空

image.png

二、原因

要使用这个 API 我们首先得明确它是个什么玩意儿,ContextLoaderListener 实际上就是一个监听器,作用就是启动 web 容器时,自动装配 ApplicationContext 的配置信息,它实现了 ServletContextListener(三大生命周期监听之一);

摘个图

Spring 实现了 Tomcat 提供的 ServletContextListener 接口,写了一个监听器来监听项目启动,一旦项目启动,会触发 ContextLoaderListener 中的特定方法 contextInitialized

也就是说 Tomcat 的 ServletContext 创建时,会调用 ContextLoaderListener 的 contextInitialized(),这个方法内部的 initWebApplicationContext()就是用来初始化 Spring 的 IOC 容器的。

  1. ServletContext 对象是 Tomcat 的;
  2. ServletContextListener 是 Tomcat 提供的接口;
  3. ContextLoaderListener 是 Spring 写的,实现了 ServletContextListener;
  4. Spring 自己写的监听器,用来创建 Spring IOC 容器;

好,现在我们知道它是个干什么的了,对,它就是用来初始化 IOC 容器的,我们都知道 IOC 里存放的我们的 Bean 对象,那么自然而然肯定有对应的方法来获取我们所需要的 Bean 对象,那么 getCurrentWebApplicationContext()方法就派上用场了。

但是为什么在我们项目中使用这个方法返回的对象是 null 呢?

首先我们得看看这是什么时候的技术?依赖于什么?

查找资料后明白了 ContextLoaderListener 如果要实现它应有的功能,是需要在 web.xml 中配置的;而 SpringBoot 中无论是以 main 方法还是 spring-boot:run 的方式执行都不跑 SpringBootServletInitializer 中的 onStartup 导致 ContextLoaderListener 没有执行。

附公司大佬的解释:

  1. ContextLoader 是 servlet2.5 那个时代的产物,它需要靠 web.xml 里去拉起 servletcontext 在启动的时候去初始化 RootWebApplicationContext(就是对上面的一个总结);
  2. 在 servlet3.0 后被 SpringBoot 自己给干掉了;

惭愧惭愧,本人入行这么些时间,都没有玩过有 web.xml 的项目,算是入行比较晚。

三、解决

  1. 最基本最官方的一个解决方案把,写一个类去实现 ApplicationContextAware 并实现它的 setApplicationContext 方法,将方法中的参数 ApplicationContext 赋值给我们自己创建的静态变量,之后使用静态变量来获取我们的 Bean 容器即可,如下文:

     1@Component
     2@Lazy(false)
     3public class ApplicationContextRegister implements ApplicationContextAware {
     4    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContextRegister.class);
     5    private static ApplicationContext APPLICATION_CONTEXT;
     6
     7    /**
     8     * 设置spring上下文  *  * @param applicationContext spring上下文  * @throws BeansException
     9     */
    10    @Override
    11    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    12        LOGGER.debug("ApplicationContext registed-->{}", applicationContext);
    13        APPLICATION_CONTEXT = applicationContext;
    14    }
    15
    16    public static ApplicationContext getApplicationContext() {
    17        return APPLICATION_CONTEXT;
    18    }
    19}
    
  2. 现在很多市场上的工具类也都有这个功能,类似 hutool 中的 SpringUtil.getBean()方法

     1	/**
     2	 * 通过class获取Bean
     3	 *
     4	 * @param <T>   Bean类型
     5	 * @param clazz Bean类
     6	 * @return Bean对象
     7	 */
     8	public static <T> T getBean(Class<T> clazz) {
     9		return getBeanFactory().getBean(clazz);
    10	}
    

标题:SpringBoot ContextLoaderListener、Servlet
作者:zzzzchen
地址:https://www.dczzs.com/articles/2022/05/05/1651751196861.html