SpringBoot启动ServletContextListener获取SpringContext

Owen Jia 2018年12月18日 454次浏览

背景

传统的tomcat方式运行的项目改造SpringBoot方式,遇到listener和springContext加载的关系问题。

传统项目配置listener方式在web.xml中,tomcat启动是会检查web.xml文件进行加载,如下:

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
	<servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
	<listener>
        <listener-class>com.tx.MonitoerLoaderListener</listener-class>
    </listener>

在项目中一般都会写个静态的公共类SpringContextHolder来获取bean,如下:

public class SpringContextHolder implements ApplicationContextAware{
	private static ApplicationContext applicationContext = null;
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		SpringContextHolder.applicationContext = applicationContext;
	}	
	public static <T> T getBean(String name) {
		assertContextInjected();
		return (T) applicationContext.getBean(name);
	}
	public static <T> T getBean(Class<T> requiredType) {
		assertContextInjected();
		return applicationContext.getBean(requiredType);
	}
}

在tomcat启动时会扫描并调用实现了ApplicationContextAware接口的setApplicationContext方法从而设置SpringContextHolder。这个会在listener加载前先执行。所以listener中可以直接使用传递SpringContextHolder的getBean(xx)方法。

项目改造成SpringBoot后,通过@ServletComponentScan注册listener,每次都是先加载listener后加载ApplicationContextAware一直无法获取到SpringContext。

springBoot改造方案

通过使用spring-web自带的类WebApplicationContextUtils的getWebApplicationContext(ServletContext xx)会返回一个WebApplicationContext,如下:

@WebListener
public class BootServiceListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {

        WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
		if(!SpringContextHolder.hasContext())
        	SpringContextHolder.setSpringContext(wac);
    }

配合@ServletComponentScan("xxx.xx.config")即可。


作者:Owen Jia,推荐关注他的博客:Owen Blog