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

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

目录
相关文章
|
9月前
|
Java Maven Android开发
微服务——SpringBoot使用归纳——Spring Boot开发环境搭建和项目启动
本文介绍了Spring Boot开发环境的搭建和项目启动流程。主要内容包括:jdk的配置(IDEA、STS/eclipse设置方法)、Spring Boot工程的构建方式(IDEA快速构建、官方构建工具start.spring.io使用)、maven配置(本地maven路径与阿里云镜像设置)以及编码配置(IDEA和eclipse中的编码设置)。通过这些步骤,帮助开发者顺利完成Spring Boot项目的初始化和运行准备。
799 0
微服务——SpringBoot使用归纳——Spring Boot开发环境搭建和项目启动
|
8月前
|
前端开发 安全 Java
Spring Boot 便利店销售系统项目分包设计解析
本文深入解析了基于Spring Boot的便利店销售系统分包设计,通过清晰的分层架构(表现层、业务逻辑层、数据访问层等)和模块化设计,提升了代码的可维护性、复用性和扩展性。具体分包结构包括`controller`、`service`、`repository`、`entity`、`dto`、`config`和`util`等模块,职责分明,便于团队协作与功能迭代。该设计为复杂企业级应用开发提供了实践参考。
327 0
|
9月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
177 0
|
9月前
|
Java 微服务 Spring
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录——使用Logger在项目中打印日志
本文介绍了如何在项目中使用Logger打印日志。通过SLF4J和Logback,可设置不同日志级别(如DEBUG、INFO、WARN、ERROR)并支持占位符输出动态信息。示例代码展示了日志在控制器中的应用,说明了日志配置对问题排查的重要性。附课程源码下载链接供实践参考。
1064 0
|
5月前
|
JSON 分布式计算 大数据
springboot项目集成大数据第三方dolphinscheduler调度器
springboot项目集成大数据第三方dolphinscheduler调度器
308 3
|
5月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
485 3
|
5月前
|
Java 关系型数据库 MySQL
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
627 2
|
5月前
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
336 2
|
5月前
|
Java 测试技术 Spring
简单学Spring Boot | 博客项目的测试
本内容介绍了基于Spring Boot的博客项目测试实践,重点在于通过测试驱动开发(TDD)优化服务层代码,提升代码质量和功能可靠性。案例详细展示了如何为PostService类编写测试用例、运行测试并根据反馈优化功能代码,包括两次优化过程。通过TDD流程,确保每项功能经过严格验证,增强代码可维护性与系统稳定性。
258 0
|
5月前
|
存储 Java 数据库连接
简单学Spring Boot | 博客项目的三层架构重构
本案例通过采用三层架构(数据访问层、业务逻辑层、表现层)重构项目,解决了集中式开发导致的代码臃肿问题。各层职责清晰,结合依赖注入实现解耦,提升了系统的可维护性、可测试性和可扩展性,为后续接入真实数据库奠定基础。
459 0

热门文章

最新文章