SpringBoot ContextLoaderListener、Servlet
一、问题
SpringBoot v2.3.2 中使用 ContextLoaderListener.getCurrentWebApplicationContext() 获取 WebApplicationContext 为空
二、原因
要使用这个 API 我们首先得明确它是个什么玩意儿,ContextLoaderListener 实际上就是一个监听器,作用就是启动 web 容器时,自动装配 ApplicationContext 的配置信息,它实现了 ServletContextListener(三大生命周期监听之一);
摘个图
Spring 实现了 Tomcat 提供的 ServletContextListener 接口,写了一个监听器来监听项目启动,一旦项目启动,会触发 ContextLoaderListener 中的特定方法 contextInitialized
也就是说 Tomcat 的 ServletContext 创建时,会调用 ContextLoaderListener 的 contextInitialized(),这个方法内部的 initWebApplicationContext()就是用来初始化 Spring 的 IOC 容器的。
- ServletContext 对象是 Tomcat 的;
- ServletContextListener 是 Tomcat 提供的接口;
- ContextLoaderListener 是 Spring 写的,实现了 ServletContextListener;
- Spring 自己写的监听器,用来创建 Spring IOC 容器;
好,现在我们知道它是个干什么的了,对,它就是用来初始化 IOC 容器的,我们都知道 IOC 里存放的我们的 Bean 对象,那么自然而然肯定有对应的方法来获取我们所需要的 Bean 对象,那么 getCurrentWebApplicationContext()方法就派上用场了。
但是为什么在我们项目中使用这个方法返回的对象是 null 呢?
首先我们得看看这是什么时候的技术?依赖于什么?
查找资料后明白了 ContextLoaderListener 如果要实现它应有的功能,是需要在 web.xml 中配置的;而 SpringBoot 中无论是以 main 方法还是 spring-boot:run 的方式执行都不跑 SpringBootServletInitializer 中的 onStartup 导致 ContextLoaderListener 没有执行。
附公司大佬的解释:
- ContextLoader 是 servlet2.5 那个时代的产物,它需要靠 web.xml 里去拉起 servletcontext 在启动的时候去初始化 RootWebApplicationContext(就是对上面的一个总结);
- 在 servlet3.0 后被 SpringBoot 自己给干掉了;
惭愧惭愧,本人入行这么些时间,都没有玩过有 web.xml 的项目,算是入行比较晚。
三、解决
-
最基本最官方的一个解决方案把,写一个类去实现 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}
-
现在很多市场上的工具类也都有这个功能,类似 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://dczzs.com/articles/2022/05/05/1651751196861.html