1. 程式人生 > >在Listener(監聽器)定時啟動的TimerTask(定時任務)中使用Spring@Service註解的bean

在Listener(監聽器)定時啟動的TimerTask(定時任務)中使用Spring@Service註解的bean

什麽 Language out 獲得 自動 location ext exti 方法

1.有時候在項目中需要定時啟動某個任務,對於這個需求,基於JavaEE規範,我們可以使用Listener與TimerTask來實現,代碼如下:

public class TestTaskListener implements ServletContextListener {
      //Context()初始化方法
      @Override
      public void contextInitialized(ServletContextEvent sce) {
          //新建一個定時管理器
          new TestTimerManager();
      }
      public TestTaskListener() {
          super();
      }
      @Override
      public void contextDestroyed(ServletContextEvent sce) {         
      }
  }

2.contextInitialized方法中新建了一個定時管理器,代碼如下:

public class TestTimerManager {
      //新建一個定時器
      Timer timer = new Timer();
      public TestTimerManager() {
          super();
          //新建一個定時任務
          TestTimerTask task = new TestTimerTask();
          //設置定時任務
          timer.schedule(task, firstTimeToStartTheTask, period);
      }   
  }

3.在定時任務的Constructor中新建了一個定時任務,其代碼如下:

@Configuration
  public class TestTimerTask extends TimerTask {
      //采用Spring框架的依賴註入
      @Autowired
      private SelectDataService selectDataService;

      public TestTimerTask() {
          super();
      }
      @Override
      public void run(){
          try {
              //訪問數據庫
              MyData myData = selectDataService.selectMyDataById(id);
          }catch(Exception ex) {
              System.out.println("定時任務出錯");
              ex.printStackTrace();
          }
      }
  }

spring是個性能非常優秀的抽象工廠,可以生產出工程所需要的實例,這裏采用Spring容器的自動註入selectDataService實例。上面代碼中,selectDataService這個類是采用Spring的@Service註解的,在項目中主要通過Spring容器註入到Controller中,其作用主要用來訪問數據庫。

運行項目將會發現NullPointerException,也就是說SelectDataService的實例沒有被註入到變量selectDataService中。那麽,這是什麽原因呢?首先來看看配置文件。
下面是web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
    <listener-class>com.test.TestTaskListener</listener-class>
</listener>

在啟動web項目時,Servlet容器(比如Tomcat)會讀web.xml配置文件中的兩個節點和,節點用來加載appliactionContext.xml(即Spring的配置文件),節點用來創建監聽器(比如TestTaskListener)實例。Listener的生命周期是由servlet容器管理的,例中的TestTaskListener是由servlet容器實例化並調用其contextInitialized方法的,但是,SelectDataService是通過@Service註解的,也就是說SelectDataService是由Spring容器管理的,在Spring容器外無法直接通過依賴註入得到Spring容器管理的bean實例的引用。為了在Spring容器外得到Spring容器管理的bean,可以使用Spring提供的工具類WebApplicationContextUtils。也就是說,可以在servlet容器管理的Listener中使用該工具類獲Spring管理的bean。

看如下代碼:

public class TestTaskListener implements ServletContextListener {
      //Context()初始化方法
      @Override
      public void contextInitialized(ServletContextEvent sce) {
          //獲得Spring容器
          WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
          //從Spring容器中獲得SelectDataServlet的實例
          SelectDataService selectDataService = ctx.getBean(SelectDataService.class);
          //新建一個定時管理器
          new TestTimerManager();
      }
      public TestTaskListener() {
          super();
      }
      @Override
      public void contextDestroyed(ServletContextEvent sce) {         
      }
  }

那麽在Listener中獲得的SelectDataService實例如何在TestTimerTask中使用呢?可以通過作為參數傳遞過去,看如下代碼:

public class TestTaskListener implements ServletContextListener {
      //Context()初始化方法
      @Override
      public void contextInitialized(ServletContextEvent sce) {
          //獲得Spring容器
          WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
          //從Spring容器中獲得SelectDataServlet的實例
          SelectDataService selectDataService = ctx.getBean(SelectDataService.class);
          //新建一個定時管理器
          new TestTimerManager(selectDataService);
      }
      public TestTaskListener() {
          super();
      }
      @Override
      public void contextDestroyed(ServletContextEvent sce) {         
      }
  }

public class TestTimerManager {
      //新建一個定時器
      Timer timer = new Timer();

      public TestTimerManager(SelectDataService selectDataService) {
          super();
          //新建一個定時任務
          TestTimerTask task = new TestTimerTask(selectDataService);
          //設置定時任務
          timer.schedule(task, firstTimeToStartTheTask, period);
      }   
  }

@Configuration
  public class TestTimerTask extends TimerTask {
      private SelectDataService selectDataService;

      public TestTimerTask(SelectDataService selectDataService) {
          super();
          this.selectDataService = selectDataService;
      }
      @Override
      public void run(){
          try {
              //訪問數據庫
              MyData myData = selectDataService.selectMyDataById(id);
          }catch(Exception ex) {
              System.out.println("定時任務出錯");
              ex.printStackTrace();
          }
      }
  }

再回到web.xml
由於Servlet容器在初始化TestTaskListener時,獲取了Spring容器,所以必須保證,在此之前,Spring容器已經初始化完成。因為Spring容器的初始化也是由Listener(ContextLoaderListener)完成,該監聽器用Spring框架提供,可以在web應用啟動時啟動Spring容器。所以,在web.xml中,要先配置ContextLoaderListener,再配置TestTaskListener。

1.有時候在項目中需要定時啟動某個任務,對於這個需求,基於JavaEE規範,我們可以使用Listener與TimerTask來實現,代碼如下:

public class TestTaskListener implements ServletContextListener {
      //Context()初始化方法
      @Override
      public void contextInitialized(ServletContextEvent sce) {
          //新建一個定時管理器
          new TestTimerManager();
      }
      public TestTaskListener() {
          super();
      }
      @Override
      public void contextDestroyed(ServletContextEvent sce) {         
      }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.contextInitialized方法中新建了一個定時管理器,代碼如下:

public class TestTimerManager {
      //新建一個定時器
      Timer timer = new Timer();
      public TestTimerManager() {
          super();
          //新建一個定時任務
          TestTimerTask task = new TestTimerTask();
          //設置定時任務
          timer.schedule(task, firstTimeToStartTheTask, period);
      }   
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3.在定時任務的Constructor中新建了一個定時任務,其代碼如下:

@Configuration
  public class TestTimerTask extends TimerTask {
      //采用Spring框架的依賴註入
      @Autowired
      private SelectDataService selectDataService;

      public TestTimerTask() {
          super();
      }
      @Override
      public void run(){
          try {
              //訪問數據庫
              MyData myData = selectDataService.selectMyDataById(id);
          }catch(Exception ex) {
              System.out.println("定時任務出錯");
              ex.printStackTrace();
          }
      }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

spring是個性能非常優秀的抽象工廠,可以生產出工程所需要的實例,這裏采用Spring容器的自動註入selectDataService實例。上面代碼中,selectDataService這個類是采用Spring的@Service註解的,在項目中主要通過Spring容器註入到Controller中,其作用主要用來訪問數據庫。

運行項目將會發現NullPointerException,也就是說SelectDataService的實例沒有被註入到變量selectDataService中。那麽,這是什麽原因呢?首先來看看配置文件。
下面是web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
    <listener-class>com.test.TestTaskListener</listener-class>
</listener>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在啟動web項目時,Servlet容器(比如Tomcat)會讀web.xml配置文件中的兩個節點和,節點用來加載appliactionContext.xml(即Spring的配置文件),節點用來創建監聽器(比如TestTaskListener)實例。Listener的生命周期是由servlet容器管理的,例中的TestTaskListener是由servlet容器實例化並調用其contextInitialized方法的,但是,SelectDataService是通過@Service註解的,也就是說SelectDataService是由Spring容器管理的,在Spring容器外無法直接通過依賴註入得到Spring容器管理的bean實例的引用。為了在Spring容器外得到Spring容器管理的bean,可以使用Spring提供的工具類WebApplicationContextUtils。也就是說,可以在servlet容器管理的Listener中使用該工具類獲Spring管理的bean。

看如下代碼:

public class TestTaskListener implements ServletContextListener {
      //Context()初始化方法
      @Override
      public void contextInitialized(ServletContextEvent sce) {
          //獲得Spring容器
          WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
          //從Spring容器中獲得SelectDataServlet的實例
          SelectDataService selectDataService = ctx.getBean(SelectDataService.class);
          //新建一個定時管理器
          new TestTimerManager();
      }
      public TestTaskListener() {
          super();
      }
      @Override
      public void contextDestroyed(ServletContextEvent sce) {         
      }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

那麽在Listener中獲得的SelectDataService實例如何在TestTimerTask中使用呢?可以通過作為參數傳遞過去,看如下代碼:

public class TestTaskListener implements ServletContextListener {
      //Context()初始化方法
      @Override
      public void contextInitialized(ServletContextEvent sce) {
          //獲得Spring容器
          WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
          //從Spring容器中獲得SelectDataServlet的實例
          SelectDataService selectDataService = ctx.getBean(SelectDataService.class);
          //新建一個定時管理器
          new TestTimerManager(selectDataService);
      }
      public TestTaskListener() {
          super();
      }
      @Override
      public void contextDestroyed(ServletContextEvent sce) {         
      }
  }

public class TestTimerManager {
      //新建一個定時器
      Timer timer = new Timer();

      public TestTimerManager(SelectDataService selectDataService) {
          super();
          //新建一個定時任務
          TestTimerTask task = new TestTimerTask(selectDataService);
          //設置定時任務
          timer.schedule(task, firstTimeToStartTheTask, period);
      }   
  }

@Configuration
  public class TestTimerTask extends TimerTask {
      private SelectDataService selectDataService;

      public TestTimerTask(SelectDataService selectDataService) {
          super();
          this.selectDataService = selectDataService;
      }
      @Override
      public void run(){
          try {
              //訪問數據庫
              MyData myData = selectDataService.selectMyDataById(id);
          }catch(Exception ex) {
              System.out.println("定時任務出錯");
              ex.printStackTrace();
          }
      }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

再回到web.xml
由於Servlet容器在初始化TestTaskListener時,獲取了Spring容器,所以必須保證,在此之前,Spring容器已經初始化完成。因為Spring容器的初始化也是由Listener(ContextLoaderListener)完成,該監聽器用Spring框架提供,可以在web應用啟動時啟動Spring容器。所以,在web.xml中,要先配置ContextLoaderListener,再配置TestTaskListener。

在Listener(監聽器)定時啟動的TimerTask(定時任務)中使用Spring@Service註解的bean