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大型互联网技术的视频免费分享给大家。

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

目录
相关文章
|
10天前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
77 0
|
11天前
|
Java 数据库连接 Maven
springBoot:项目建立&配置修改&yaml的使用&resource 文件夹(二)
本文档介绍了如何创建一个基于Maven的项目,并配置阿里云仓库、数据库连接、端口号、自定义启动横幅及多环境配置等。同时,详细说明了如何使用YAML格式进行配置,以及如何处理静态资源和模板文件。文档还涵盖了Spring Boot项目的`application.properties`和`application.yaml`文件的配置方法,包括设置数据库驱动、URL、用户名、密码等关键信息,以及如何通过配置文件管理不同环境下的应用设置。
|
9天前
|
JavaScript 前端开发 Java
解决跨域问题大集合:vue-cli项目 和 java/springboot(6种方式) 两端解决(完美解决)
这篇文章详细介绍了如何在前端Vue项目和后端Spring Boot项目中通过多种方式解决跨域问题。
178 1
解决跨域问题大集合:vue-cli项目 和 java/springboot(6种方式) 两端解决(完美解决)
|
5天前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
9天前
|
Java Maven Spring
springboot学习一:idea社区版本创建springboot项目的三种方式(第三种为主)
这篇文章介绍了在IntelliJ IDEA社区版中创建Spring Boot项目的三种方法,特别强调了第三种方法的详细步骤。
30 0
springboot学习一:idea社区版本创建springboot项目的三种方式(第三种为主)
|
9天前
|
监控 Java Maven
springboot学习二:springboot 初创建 web 项目、修改banner、热部署插件、切换运行环境、springboot参数配置,打包项目并测试成功
这篇文章介绍了如何快速创建Spring Boot项目,包括项目的初始化、结构、打包部署、修改启动Banner、热部署、环境切换和参数配置等基础操作。
44 0
|
10天前
|
Java 应用服务中间件 Maven
SpringBoot Maven 项目打包的艺术--主清单属性缺失与NoClassDefFoundError的优雅解决方案
SpringBoot Maven 项目打包的艺术--主清单属性缺失与NoClassDefFoundError的优雅解决方案
122 0
|
12天前
|
Java Shell C++
Springboot加载注入bean的方式
本文详细介绍了Spring Boot中Bean的装配方法。首先讲解了使用@Component、@Service、@Controller、@Repository等注解声明Bean的方式,并解释了这些注解之间的关系及各自适用的层次。接着介绍了通过@Configuration和@Bean注解定义Bean的方法,展示了其灵活性和定制能力。最后讨论了@Component与@Bean的区别,并提供了在Spring Boot应用中装配依赖包中Bean的三种方法:使用@ComponentScan注解扫描指定包、使用@Import注解导入特定Bean以及在spring.factories文件中配置Bean。
|
Java 应用服务中间件 Maven
传统maven项目和现在spring boot项目的区别
Spring Boot:传统 Web 项目与采用 Spring Boot 项目区别
475 0
传统maven项目和现在spring boot项目的区别
|
XML Java 数据库连接
创建springboot项目的基本流程——以宠物类别为例
创建springboot项目的基本流程——以宠物类别为例
149 0
创建springboot项目的基本流程——以宠物类别为例