使用自动注入属性注解 报错得到空指针异常

10 篇文章 0 订阅

在Listener(监听器)定时启动的TimerTask(定时任务)中使用Spring@Service注解的bean
使用自动注入属性注解 报错得到空指针异常

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) {         
      }
  }

      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。

                          评论
                          添加红包

                          请填写红包祝福语或标题

                          红包个数最小为10个

                          红包金额最低5元

                          当前余额3.43前往充值 >
                          需支付:10.00
                          成就一亿技术人!
                          领取后你会自动成为博主和红包主的粉丝 规则
                          hope_wisdom
                          发出的红包
                          实付
                          使用余额支付
                          点击重新获取
                          扫码支付
                          钱包余额 0

                          抵扣说明:

                          1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
                          2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

                          余额充值