这个东西源于这种需求: 一个应用丢到服务其后, 不管用户有没有访问项目, 这个后台线程都必须给我跑, 而且这个线程还调用了 Spring 注入的 bean, 这样自然就会想到去监听 Servlet 的状态, 当 Servlet 初始化完毕后会调用 ServletContextListener 中的 contextInitialized 方法, 所以可以创建一个监听器继承 ServletContextListener 类来监听 Servlet 的状态, 在 contextInitialized 方法中来启动后台的线程, 但是如何使用 Spring 注入的 bean 呢? 所以必须确保在启动线程前 Spring 容器必须初始化完毕, Spring 的初始化也是有 Listener 完成的, 所以这里特别注意的是自定的监听器必须放在 Spring 的监听器之后 (很重要), 否则无法获取 bean 属性, 会报空指针异常!
1. 创建监听器
- package com.hhu.listener;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import com.hhu.threads.DiagnosisThread;
- /**
- * 这个监听器在 web 容器初始化后就立刻启用了
- * @author Weiguo Liu
- * @data 2017 年 11 月 30 日
- */
- public class ContextListener implements ServletContextListener {
- @Override
- public void contextInitialized(ServletContextEvent sce) {
- System.out.println("加载应用程序...");
- // StationService stationService = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()).getBean(StationService.class);
- // System.out.println("stationService=" + stationService);
- /*
- * 创建诊断线程并启动
- */
- DiagnosisThread dt = new DiagnosisThread();
- dt.start();
- System.out.println("Listener 继续执行");
- }
- @Override
- public void contextDestroyed(ServletContextEvent sce) {
- // TODO Auto-generated method stub
- }
- }
2.Web.xml 配置启动顺序
- <!-- 这里不能少, web 启动后会按这个位置寻找 Spring 的配置文件 -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:spring/spring-*.xml
- </param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.Web.context.ContextLoaderListener</listener-class>
- </listener>
- <listener>
- <listener-class>
- com.hhu.listener.ContextListener
- </listener-class>
- </listener>
- <servlet>
- <servlet-name>springDispatcherServlet</servlet-name>
- <servlet-class>org.springframework.Web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:spring/spring-*.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>springDispatcherServlet</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
3. 写一个获取 Spring Bean 的工具类
由于 ServletContextListener 并不被 Spring 管理, 所以我们不能使用 @Autowired 注解来获取相应的 bean 属性, 而是利用 ApplicationContext 来获取 Bean, 代码如下
- package com.hhu.util;
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- /**
- * 使用 getBean 可以获取对应的 bean, 自己的的手动进行类型强转
- * 创建获取 SpringBean 的工具类
- * @author Weiguo Liu
- *
- */
- public class SpringBeanUtil implements ApplicationContextAware {
- private static ApplicationContext applicationContext = null;
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- SpringBeanUtil.applicationContext = applicationContext;
- }
- public static Object getBeanByName(String beanName) {
- if (applicationContext == null) {
- return null;
- }
- return applicationContext.getBean(beanName);
- }
- public static <T> T getBean(Class<T> type) {
- return applicationContext.getBean(type);
- }
- }
这样以后就可以在后台线程中愉快的获取 Spring bean 了, 第三个工具类很强大, 只要 Spring 初始化后, 不管所在类是否被 Spring 管理, 都可以使用如下的方式获取
bean 的类型 bean 的名字 = (bean 的类型)SpringBeanUtil.getBeanByName("bean 的名字");
做的越多, 也就发现自己不懂的越多, 还是要深入理解其原理啊
来源: http://www.bubuko.com/infodetail-2973012.html