在spring项目启动时,有一些功能需要在项目启动时加载的,一般我们都会写一个实现ServletContextListener接口的监听类来完成
public class CollectorBoot implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("启动订阅启动类...");
String[] topicTypeArr = ConsumerResource.TOPIC_TYPE.split(",");
for (int i = 0; i < topicTypeArr.length; i++) {
if("1".equals(topicTypeArr[i])){ startPointStorageConsumer();}//点位设备数据入库
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
if(pointStorageCollector!=null){pointStorageCollector.close();}
}
/**
* @描述 点位设备数据入库,KAFKA订阅
*/
private void startPointStorageConsumer() {
//获取bean
PointStorageConsumer pointStorageConsumer = SpringApplicationContext.getBean(pointStorageConsumer .class);
this.pointStorageCollector = new LogCollector(POINT_DATA_PLUS,threadNum,pointStorageConsumer);
try {
this.pointStorageCollector.start();
logger.info("点位设备数据入库,KAFKA订阅成功!");
} catch (Exception e) {
logger.error("点位设备数据入库,KAFKA订阅失败!", e);
e.printStackTrace();
}
}
}
而在项目中,我们会有一个实现ApplicationContextAware接口来获取上下文bean的类
public class SpringApplicationContext implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
// name表示注入的注解name名
public static Object getBean(String name) {
return context.getBean(name);
}
// T表示注入的类
public static <T> T getBean(Class<T> c){
return context.getBean(c);
}
}
项目启动时监听类CollectorBoot通过调用SpringApplicationContext.getBean方法获取pointStorageConsumer,最后实现pointStorageConsumer业务的kafka主题订阅。
最近在把这套代码搬到一个spring boot的项目中后,突然发现一直获取不到bean,我们先看下原来是怎么处理的:
原spring项目是在web.xml中配置监听类,tomcat启动时检查web.xml的信息加载ContextLoaderListener,此时会Spring容器会自动把上下文环境对象调用ApplicationContextAware接口中的setApplicationContext方法,之后再调用其他监听类,CollectorBoot通过SpringApplicationContext.getBean就可以获取到spring容器中的bean了
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_config_*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Open Session In View Filter -->
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
而在spring boot中,却获取不到,初步怀疑是类加载顺序的不同导致的。debug了一下,发现通过@ServletComponentScan注册listener,每次都是先加载listener后再加载ApplicationContextAware,因此导致每次获取的bean都是null,查了一下其他人的情况,最后发现可以通过WebApplicationContextUtils来获取SpringContext,因此把上面的监听类改成如下情况,至此顺利在listener中获取到想要的bean了
@WebListener
public class CollectorBoot implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("启动订阅启动类...");
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
String[] topicTypeArr = ConsumerResource.TOPIC_TYPE.split(",");
for (int i = 0; i < topicTypeArr.length; i++) {
if("1".equals(topicTypeArr[i])){ startPointStorageConsumer(wac);}//点位设备数据入库
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
if(pointStorageCollector!=null){pointStorageCollector.close();}
}
/**
* @描述 点位设备数据入库,KAFKA订阅
*/
private void startPointStorageConsumer(WebApplicationContext wac) {
//获取bean
PointStorageConsumer pointStorageConsumer = wac.getBean(PointStorageConsumer.class);
this.pointStorageCollector = new LogCollector(POINT_DATA_PLUS,threadNum,pointStorageConsumer);
try {
this.pointStorageCollector.start();
logger.info("点位设备数据入库,KAFKA订阅成功!");
} catch (Exception e) {
logger.error("点位设备数据入库,KAFKA订阅失败!", e);
e.printStackTrace();
}
}
}
- 本文链接: https://www.acgclub.xyz/archives/springboot在servletcontextlistener中获取上下文的bean
- 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!