SpringBoot项目使用多线程处理任务时无法通过Autowired注入bean-阿里云开发者社区

开发者社区> dasein58> 正文

SpringBoot项目使用多线程处理任务时无法通过Autowired注入bean

简介:   最近在做一个“温湿度控制”的项目,项目要求通过用户设定的温湿度数值和实时采集到的数值进行比对分析,因为数据的对比与分析是一个通过前端页面控制的定时任务,经理要求在用户开启定时任务时,单独开启一个线程进行数据的对比分析,并将采集到的温湿度数值存入数据库中的历史数据表,按照我们正常的逻辑应该是用户在请求开启定时任务时,前端页面通过调用后端接口,创建一个新的线程来执行定时任务,然后在线程类中使用 @Autowired 注解注入保存历史数据的service层,在线程类中调用service层保存历史数据的方法实现温湿度数据的保存,这时就出现了一个很尴尬的问题,在新开启的线程中使用 @Autowire
+关注继续查看

  最近在做一个“温湿度控制”的项目,项目要求通过用户设定的温湿度数值和实时采集到的数值进行比对分析,因为数据的对比与分析是一个通过前端页面控制的定时任务,经理要求在用户开启定时任务时,单独开启一个线程进行数据的对比分析,并将采集到的温湿度数值存入数据库中的历史数据表,按照我们正常的逻辑应该是用户在请求开启定时任务时,前端页面通过调用后端接口,创建一个新的线程来执行定时任务,然后在线程类中使用 @Autowired 注解注入保存历史数据的service层,在线程类中调用service层保存历史数据的方法实现温湿度数据的保存,这时就出现了一个很尴尬的问题,在新开启的线程中使用 @Autowired 注解无法注入需要的bean(即:保存历史数据的service层),程序一直在报 NullPointerException 。

  这是controller层,方法 startExperiment 和 stopExperiment 分别是开始定时任务和停止定时任务的方法,getData方法不属于本次讨论范围,不用管

  如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

  package com.backstage.controller;

  import com.alibaba.fastjson.JSONObject;

  import com.backstage.entity.JsonResponse;

  import com.backstage.entity.Threshold;

  import com.backstage.service.MainPageService;

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.web.bind.annotation.RequestMapping;

  import org.springframework.web.bind.annotation.RestController;

  import javax.servlet.http.HttpServletRequest;

  /**

  * @ProjectName:

  * @Package: com.backstage.controller

  * @ClassName: MainPageController

  * @Description: 主页面相关操作控制器

  * @Author: wangzhilong

  * @CreateDate: 2021/8/29 9:49

  * @Version: 1.0

  */

  @RestController

  @RequestMapping("/main")

  public class MainPageController {

  @Autowired

  private MainPageService mainPageService;

  /**

  * 开始实验

  *

  * @param threshold

  */

  @RequestMapping("/startExperiment")

  public JsonResponse startExperiment(HttpServletRequest request, Threshold threshold) {

  return mainPageService.startExperiment(request, threshold);

  }

  /**

  * 停止实验

  */

  @RequestMapping("/stopExperiment")

  public JsonResponse stopExperiment() {

  return mainPageService.stopExperiment();

  }

  /**

  * 获取实时数据

  *

  * @return

  */

  @RequestMapping("/getData")

  public JSONObject getData() {

  return null;

  }

  }

  service 层接口代码,没什么好说的,直接上代码:

  package com.backstage.service;

  import com.alibaba.fastjson.JSONObject;

  import com.backstage.entity.JsonResponse;

  import com.backstage.entity.Threshold;

  import javax.servlet.http.HttpServletRequest;

  /**

  * @ProjectName:

  * @Package: com.backstage.service

  * @ClassName: MainPageService

  * @Description: 主页面相关操作业务层接口

  * @Author: wangzhilong

  * @CreateDate: 2021/8/29 9:51

  * @Version: 1.0

  */

  public interface MainPageService {

  /**

  * 开始实验

  *

  * @param threshold

  */

  JsonResponse startExperiment(HttpServletRequest request, Threshold threshold);

  /**

  * 停止实验

  */

  JsonResponse stopExperiment();

  /**

  * 获取实时数据

  *

  * @return

  */

  JSONObject getData();

  }

  service 层实现类代码,关于springboot项目使用多线程进行业务处理不属于本章节的讨论范围,如有需要,请留言,我会在看到留言后第一时间更新相关技术文章,由于这里删除了一些与本章节无关的代码,如果复制到买二手手游开发工具内有报错问题,麻烦大家提醒我一下,以便修改,非常感谢

  package com.backstage.servicepl;

  import com.alibaba.fastjson.JSONObject;

  import com.backstage.entity.*;

  import com.backstage.monitor.TimingMonitoring;

  import com.backstage.service.*;

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.context.annotation.Bean;

  import org.springframework.scheduling.Trigger;

  import org.springframework.scheduling.TriggerContext;

  import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

  import org.springframework.scheduling.support.CronTrigger;

  import org.springframework.stereotype.Service;

  import javax.servlet.http.HttpServletRequest;

  import java.text.SimpleDateFormat;

  import java.util.Date;

  import java.util.List;

  import java.util.concurrent.ScheduledFuture;

  /**

  * @ProjectName:

  * @Package: com.backstage.servicepl

  * @ClassName: MainPageServiceImpl

  * @Description: 主页面相关操作业务层实现类

  * @Author: wangzhilong

  * @CreateDate: 2021/8/29 9:51

  * @Version: 1.0

  */

  @Service

  public class MainPageServiceImpl implements MainPageService {

  @Autowired

  private ThreadPoolTaskScheduler threadPoolTaskScheduler;

  private ScheduledFuture future2;

  @Bean

  public ThreadPoolTaskScheduler threadPoolTaskScheduler() {

  return new ThreadPoolTaskScheduler();

  }

  /**

  * 开始实验

  *

  * @param threshold

  */

  @Override

  public JsonResponse startExperiment(HttpServletRequest request, Threshold threshold) {

  TimingMonitoring timingMonitoring=new TimingMonitoring();

  timingMonitoring.setThreshold(threshold, list, experiment.getId(), experimentData.getId());

  future2=threadPoolTaskScheduler.schedule(new TimingMonitoring(), new Trigger() {

  @Override

  public Date nextExecutionTime(TriggerContext triggerContext) {

  //设置定时任务的执行时间为3秒钟执行一次

  return new CronTrigger("0/10 ?").nextExecutionTime(triggerContext);

  }

  });

  return new JsonResponse(0,"开始实验!");

  }

  /**

  * 停止实验

  */

  @Override

  public JsonResponse stopExperiment() {

  if (future2 !=null) {

  experimentService.upd(getTime());

  future2.cancel(true);

  }

  return new JsonResponse(0,"结束实验!");

  }

  /**

  * 获取实时数据

  *

  * @return

  */

  @Override

  public JSONObject getData() {

  return null;

  }

  protected String getTime() {

  SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

  return format.format(new Date());

  }

  }

  如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

  重点,线程类代码,大家注意看,我在代码最开始使用了spring的 @Autowired 注解注入需要的service,可在调用service中的add方法时,程序报空指针异常,一直认为是add方法或者sql语句有问题,找了一上午,也没发现任何问题,后来单独调用这个add方法是可以正常插入数据的,唯独在这个线程类中调用时报错,感觉和线程有莫大的关系,百度一搜,还真找到了,原来,在线程中为了线程安全,是防注入的,没办法,要用到这个类啊。只能从bean工厂里拿个实例了,继续往下看

  package com.backstage.monitor;

  import com.backstage.entity.DetailedData;

  import com.backstage.entity.Threshold;

  import com.backstage.entity.ValveValue;

  import com.backstage.service.DetailedDataService;

  import java.text.SimpleDateFormat;

  import java.util.Date;

  import java.util.List;

  /**

  * @ProjectName:

  * @Package: com.backstage.monitor

  * @ClassName: TimingMonitoring

  * @Description: 定时监测温(湿)度 数据

  * @Author: wangzhilong

  * @CreateDate: 2021/8/29 10:11

  * @Version: 1.0

  */

  public class TimingMonitoring implements Runnable{

  //历史数据业务层接口

  @Autowired

  public DetailedDataService detailedDataService;

  private Threshold threshold; //阈值实体类

  private List settingData; //设定的温湿度数据

  private Integer id; //实验记录id

  private Integer dataId; //历史数据主表id

  public void setThreshold(Threshold threshold, List settingData, Integer id, Integer dataId) {

  this.threshold=threshold;

  this.settingData=settingData;

  this.id=id;

  this.dataId=dataId;

  }

  @Override

  public void run() {

  //模拟从PLC获取到的数据

  String data="001,50.5,002,37,003,45.6,004,40,005,55.2,006,58";

  if (data==null || data.trim()=="") {

  return; //若获取到的数据为空,则直接停止该方法的执行

  }

  double temperature=0.0; //温度

  double humidity=0.0; //湿度

  Integer type=null; //数据类型,1是温度,2是湿度

  //解析数据,并将数据保存到历史数据数据库

  String[] str=data.split(",");

  SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS");

  for (int i=0; i < str.length; i++) {

  if (i==1 || i==5 || i==9) { //温度

  type=1;

  temperature +=Double.parseDouble(str[i]);

  //System.out.println("温度" + i + " -》 " + str[i-1] + ":" + str[i]);

  detailedDataService.add(new DetailedData(null, type, Double.parseDouble(str[i]), format.format(new Date()), str[i - 1], dataId));

  }

  if (i==3 || i==7 || i==11) { //湿度

  type=2;

  humidity +=Double.parseDouble(str[i]);

  //System.out.println("湿度" + i + " -》 " + str[i-1] + ":" + str[i]);

  detailedDataService.add(new DetailedData(null, type, Double.parseDouble(str[i]), format.format(new Date()), str[i - 1], dataId));

  }

  }

  }

  /**

  * 获取当前时间,精确到毫秒

  * @return

  */

  protected String getTime() {

  SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS");

  return format.format(new Date());

  }

  }

  获取bean对象的工具类,既然程序无法通过注解拿到需要的bean,那就只好自己写个工具类来获取喽,下面是工具类代码

  package com.backstage.config;

  import org.springframework.beans.BeansException;

  import org.springframework.context.ApplicationContext;

  import org.springframework.context.ApplicationContextAware;

  import org.springframework.stereotypeponent;

  /**

  * @ProjectName:

  * @Package: com.backstage.config

  * @ClassName: ApplicationContextProvider

  * @Description: 获取bean对象的工具类

  * @Author: wangzhilong

  * @CreateDate: 2021/8/31 13:26

  * @Version: 1.0

  */

  /**

  * Author:ZhuShangJin

  * Date:2021/7/3

  */

  @Component

  public class ApplicationContextProvider implements ApplicationContextAware {

  /**

  * 上下文对象实例

  */

  private static ApplicationContext applicationContext;

  @Override

  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

  this.applicationContext=applicationContext;

  }

  /**

  * 获取applicationContext

  *

  * @return

  */

  public static ApplicationContext getApplicationContext() {

  return applicationContext;

  }

  /**

  * 通过name获取 Bean.

  *

  * @param name

  * @return

  */

  public static Object getBean(String name) {

  return getApplicationContext().getBean(name);

  }

  /**

  * 通过class获取Bean.

  *

  * @param clazz

  * @param

  * @return

  */

  public static T getBean(Class clazz) {

  return getApplicationContext().getBean(clazz);

  }

  /**

  * 通过name,以及Clazz返回指定的Bean

  *

  * @param name

  * @param clazz

  * @param

  * @return

  */

  public static T getBean(String name, Class clazz) {

  return getApplicationContext().getBean(name, clazz);

  }

  }

  这样呢,就可以在线程类中写一个无参的构造方法,在构造方法中,通过调用工具类中的 getBean() 方法就可以拿到实例了,程序在调用这个线程类时,会自动调用其无参的构造方法,在构造方法中我们将需要的bean对象注入,然后就可以正常使用了,下边是线程类修改后的代码,由于别的地方没有改动,所以这里只给大家改动的代码,省得大家看到一大堆代码头疼。

  public TimingMonitoring() {

  //new的时候注入需要的bean

  this.detailedDataService=ApplicationContextProvider.getBean(DetailedDataService.class);

  }

  如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

  好了,至此呢,问题就得到解决了,文章中如错误或不足,请指出,不胜感激

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
项目心得:广度遍历搜索部门处理业务
部门树节点 平常在做后台管理系统的时候,多多少少都会涉及部门管理,部门有上下级,所以架构会呈现出树形,下图是一个简单的部门节点图:                         这个和平时的二叉树很像,如果部门比较多的话,那么这个树就会很复杂。
800 0
2014秋C++第13周项目2参考-成绩处理
课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,课程资源在云学堂“贺老师课堂”同步展示,使用的帐号请到课程主页中查看。  【项目2- 成绩处理】在数组score中将要存储某小组C++程序设计的成绩,请设计实现下面的各功能函数,并在main函数中调用,组合成一个完整的应用:(1)输入小组人数及成绩,要保证成绩在0-10
1094 0
C#网络爬虫--多线程处理强化版
上次做了一个帮公司妹子做了爬虫,不是很精致,这次公司项目里要用到,于是有做了一番修改,功能添加了网址图片采集,下载,线程处理界面网址图片下载等。 说说思路:首相获取初始网址的所有内容 在初始网址采集图片 去初始网址采集链接 把采集到的链接放入队列 继续采集图片,然后继续采集链接,无限循环 还是上...
996 0
2014秋C++第12周项目2参考-OJ平台题目中多种输入形式的处理
课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,课程资源在云学堂“贺老师课堂”同步展示,使用的帐号请到课程主页中查看。  【项目2-OJ平台题目中多种输入形式的处理】看教学视频或“OJ平台题目中不同形式输入的处理”,完成平台中的一组题目,分别对应不同输入的形式的问题。 A: 求N组数的最大公约数 Descriptio
1180 0
+关注
780
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载