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

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

目录
相关文章
|
29天前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
135 0
|
1月前
|
算法 NoSQL Java
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
71 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
|
1月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
72 5
|
1月前
|
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。
|
1月前
|
Java
SpringBoot线程问题
SpringBoot线程问题
25 0
|
3月前
|
前端开发 JavaScript 大数据
React与Web Workers:开启前端多线程时代的钥匙——深入探索计算密集型任务的优化策略与最佳实践
【8月更文挑战第31天】随着Web应用复杂性的提升,单线程JavaScript已难以胜任高计算量任务。Web Workers通过多线程编程解决了这一问题,使耗时任务独立运行而不阻塞主线程。结合React的组件化与虚拟DOM优势,可将大数据处理等任务交由Web Workers完成,确保UI流畅。最佳实践包括定义清晰接口、加强错误处理及合理评估任务特性。这一结合不仅提升了用户体验,更为前端开发带来多线程时代的全新可能。
67 1
|
3月前
|
存储 监控 Java
|
3月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
43 0
|
3月前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
119 0
|
3月前
|
Java 测试技术 Android开发
Android项目架构设计问题之构造一个Android中的线程池如何解决
Android项目架构设计问题之构造一个Android中的线程池如何解决
25 0
下一篇
无影云桌面