解决SpringBoot2多线程无法注入的问题

 更新时间:2020年8月27日 08:33  点击:1707

1、情况描述

使用springboot2多线程,线程类无法实现自动注入需要的bean,解决思路,通过工具类获取需要的bean

如下

package com.ps.uzkefu.apps.ctilink.handler;
 
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.ps.uzkefu.apps.callcenter.entity.CallRecord;
import com.ps.uzkefu.apps.callcenter.service.CallRecordService;
import com.ps.uzkefu.apps.ctilink.init.ApplicationContextProvider;
import com.ps.uzkefu.apps.ctilink.ommodel.CallCdr;
import com.ps.uzkefu.apps.ctilink.ommodel.Cdr;
import com.ps.uzkefu.apps.ctilink.rediskey.CdrType;
import com.ps.uzkefu.apps.ctilink.rediskey.EventType;
import com.ps.uzkefu.apps.ctilink.rediskey.RedisKeyPrefix;
import com.ps.uzkefu.apps.oms.account.entity.User;
import com.ps.uzkefu.apps.oms.account.service.UserService;
import com.ps.uzkefu.util.UUIDUtil;
import com.ps.uzkefu.utils.PhoneModel;
import com.ps.uzkefu.utils.PhoneUtils;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
 
import java.util.Date;
import java.util.Objects;
 
/**
 * Author:ZhuShangJin
 * Date:2018/6/26
 */
public class CdrHandler implements Runnable {
 
 public Cdr cdr;
 //无法自动注入
 public RedissonClient redissonClient;
 //无法自动注入
 public UserService userService;
 //无法自动注入
 public CallRecordService callRecordService;
 
 public CdrHandler() {
  //new的时候注入需要的bean
  this.redissonClient = ApplicationContextProvider.getBean(RedissonClient.class);
  this.userService = ApplicationContextProvider.getBean(UserService.class);
  this.callRecordService = ApplicationContextProvider.getBean(CallRecordService.class);
 }
 
 public RedissonClient getRedissonClient() {
  return redissonClient;
 }
 
 public void setRedissonClient(RedissonClient redissonClient) {
  this.redissonClient = redissonClient;
 }
 
 public Cdr getCdr() {
  return cdr;
 }
 
 public void setCdr(Cdr cdr) {
 
  this.cdr = cdr;
 }
 
 public UserService getUserService() {
  return userService;
 }
 
 public void setUserService(UserService userService) {
  this.userService = userService;
 }
 
 public CallRecordService getCallRecordService() {
  return callRecordService;
 }
 
 public void setCallRecordService(CallRecordService callRecordService) {
  this.callRecordService = callRecordService;
 }
 
 @Override
 public void run() {
  if (this.getCdr().getOuter() != null) {
   saveOuterCdr();
  } else if (this.getCdr().getVisitor() != null) {
   saveVistorCdr();
  }
 }
 
 private void saveOuterCdr() {
  // 外呼 通话结束
  CallCdr callCdr = null;
  RBucket<CallCdr> bucket = redissonClient.getBucket(RedisKeyPrefix.CALL_OUTER_CDR + this.getCdr().getOuter().getId() + "_" + cdr.getCpn());
  callCdr = bucket.get();
  callCdr.setRedisKey(RedisKeyPrefix.CALL_OUTER_CDR + this.getCdr().getOuter().getId() + "_" + cdr.getCpn());
  callCdr.setLastEvent(EventType.BYE);
  callCdr.setLastEventTime(new Date());
  callCdr.setTalkLength(Integer.parseInt(this.getCdr().getDuration()));
  callCdr.setTrunkNum(this.getCdr().getTrunkNumber());
  callCdr.setHangupTime(new Date());
  callCdr.setRecord(this.getCdr().getRecording());
  if (callCdr.getAnsweredTime() == null){
   callCdr.setCallTime(callCdr.getHangupTime());
  }else {
   long time = callCdr.getAnsweredTime().getTime() - callCdr.getRingLength()*1000;
   callCdr.setCallTime(new Date(time));
  }
  //todo 保存到数据库
  User user = userService.selectOne(new EntityWrapper<User>().eq("extension", callCdr.getExtensionNum() + ""));
  callCdr.setUserName(user.getUserName());
  callCdr.setCorpCode(user.getCorpCode());
  callCdr.setCreater(user.getId());
  callCdr.setId(UUIDUtil.genUUID());
  callCdr.setCreateTime(new Date());
  PhoneModel phoneModel = PhoneUtils.getPhoneModel(callCdr.getCustomerPhone());
  if (phoneModel != null) {
   callCdr.setCustomerCity(phoneModel.getCityName());
   callCdr.setCustomerProvince(phoneModel.getProvinceName());
  }
  callCdr.setCallId(System.currentTimeMillis() + "" + callCdr.getCallId());
  bucket.set(callCdr);
  CallRecord callRecord = callCdr;
  boolean result = callRecordService.insert(callRecord);
  if (result) {
   bucket.delete();
  } 
 } 

 private void saveVistorCdr() {
  CallCdr callCdr = null;
  RBucket<CallCdr> bucket = redissonClient.getBucket(RedisKeyPrefix.CALL_VISITOR_CDR + this.getCdr().getVisitor().getId() + "_" + cdr.getTrunkNumber());
  callCdr = bucket.get();
  callCdr.setRedisKey(RedisKeyPrefix.CALL_VISITOR_CDR + this.getCdr().getVisitor().getId() + "_" + cdr.getTrunkNumber());
  callCdr.setRecord(this.getCdr().getRecording());
  PhoneModel phoneModel = PhoneUtils.getPhoneModel(callCdr.getCustomerPhone());
  if (phoneModel != null) {
   callCdr.setCustomerCity(phoneModel.getCityName());
   callCdr.setCustomerProvince(phoneModel.getProvinceName());
  }
  callCdr.setCallId(System.currentTimeMillis() + "" + callCdr.getCallId());
  callCdr.setId(UUIDUtil.genUUID());
  //来电 通话结束 外部电话 呼入 接入分机的童虎记录
  if (Objects.equals(CdrType.IN, this.getCdr().getType()) && this.getCdr().getCdpn().length() == 5) {
   callCdr.setExtensionNum(Integer.parseInt(this.getCdr().getCdpn()));
   User user = userService.selectOne(new EntityWrapper<User>().eq("extension", callCdr.getExtensionNum() + ""));
   callCdr.setUserName(user.getUserName());
   callCdr.setCorpCode(user.getCorpCode());
   callCdr.setCreater(user.getId());
   if (Objects.equals(EventType.RING, callCdr.getLastEvent())) {
    if (StringUtils.isBlank(this.getCdr().getRecording())) {
     //用户在坐席未接来电时 未接来电无录音 挂机
     int ringLength = (int) ((new Date().getTime() - callCdr.getLastEventTime().getTime()) / 1000);
     callCdr.setRingLength(ringLength);
     callCdr.setTalkLength(0);
    } else {
     //特殊情况 坐席接听后立马挂掉
     callCdr.setTalkLength(Integer.parseInt(this.getCdr().getDuration()));
     callCdr.setRingLength(-1);
     callCdr.setLastEvent(CdrType.UNUSUAL);
    }
   } else {
    //正常情况
    callCdr.setTalkLength(Integer.parseInt(this.getCdr().getDuration()));
   }
  } else if (Objects.equals(CdrType.IN, this.getCdr().getType()) && this.getCdr().getCdpn().length() != 5) {
   //客服没接到
   callCdr.setExtensionNum(0);
   callCdr.setUserName("未接到");
   callCdr.setCorpCode(this.getCdr().getCdpn());
   callCdr.setCreater("未接到");
   callCdr.setTalkLength(0);
   int ringLength = (int) ((new Date().getTime() - callCdr.getCallTime().getTime())/1000);
   callCdr.setRingLength(ringLength);
  }
  callCdr.setCreateTime(new Date());
  callCdr.setHangupTime(new Date());
  bucket.set(callCdr);
 
  if (Objects.equals(CdrType.IN, this.getCdr().getType())
    && this.getCdr().getCdpn().length() == 5
    && Objects.equals(EventType.RING, callCdr.getLastEvent())
    && StringUtils.isNotBlank(this.cdr.getRecording())) {
 
  }else if(Objects.equals(CdrType.UNUSUAL,callCdr.getLastEvent())){
 
  }else {
   CallRecord callRecord = callCdr;
   boolean result = callRecordService.insert(callRecord);
   if (result) {
    bucket.delete();
   }
  }
 }
}

2、获取bean的工具类

package com.ps.uzkefu.apps.ctilink.init;
 
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
/**
 * Author:ZhuShangJin
 * Date:2018/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 <T>
  * @return
  */
 public static <T> T getBean(Class<T> clazz) {
  return getApplicationContext().getBean(clazz);
 }
 
 /**
  * 通过name,以及Clazz返回指定的Bean
  *
  * @param name
  * @param clazz
  * @param <T>
  * @return
  */
 public static <T> T getBean(String name, Class<T> clazz) {
  return getApplicationContext().getBean(name, clazz);
 }
}

3、通过工具类的getBean方法即可获取bean

补充知识:关于Spring/SpringBoot在静态工具类中注入Service的解决方案

前言今天博主将为大家分享:关于Spring/SpringBoot在静态工具类中注入Service的解决方案!不喜勿喷,如有异议欢迎讨论!

最近遇到了需要在工具类中注入Service,由于工具类中方法一般都是静态的,所以要求该属性也要是静态的(Service)。但是由于Spring/SpringBoot正常情况下不能支持注入静态属性(会报空指针异常)。主要原因在于:Spring的依赖注入实际上是依赖于Set方法进行注入值的,Spring是基于对象层面的依赖注入,而静态属性/静态变量实际上是属于类的。

解决方案:

给当前的工具类加上@Component,使其成为一个bean对象

声明一个静态的属性(加上注解@Autowired),一个非静态的属性。

声明一个返回值为void并且不能抛出异常的方法,在其中将非静态属性赋值给静态属性。该方法上加上注解@PostConstruct

这样就将service的值注入了进来。示例代码如下:

/**
*
*@Description: 关于Spring/SpringBoot在静态工具类中注入Service的解决方案
*@ClassName: XXUtils.java
*@author ChenYongJia
*@Date 2019年6月26日 晚上21:20
*@Email chen87647213@163.com
*/

@Component
public class XXUtils {

@Autowired
private SpecialLogSevice sevice; 

private static SpecialLogSevice specialLogSevice; 

@PostConstruct 
public void init() {
 specialLogSevice = sevice;
} 

//下面的内容就省略了,需要调用specialLogSevice打点就行了

}

在上述代码中@PostConstruct是Java EE5规范之后,Servlet新增的两个影响servlet声明周期的注解之一,另外一个是@PreConstruct。这两个都可以用来修饰一个非静态的返回值为void的方法,并且该方法不能抛出异常。

被@PostConstruct注解修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet中的init方法。被该注解修饰的方法会在构造器执行之后,init方法执行之前执行。Spring中允许开发者在受理的Bean中去使用它,当IOC容器被实例化管理当前bean时,被该注解修饰的方法会执行,完成一些初始化的工作。

被PreConstruct注解修饰的方法会在服务器卸载Servlet的时候运行,类似于Servlet中的destroy方法。被该注解修饰的方法会在destroy方法执行之后,Servlet彻底卸载之前执行。

到这里:关于Spring/SpringBoot在静态工具类中注入Service的解决方案!分享完毕了,快去试试吧!希望大家多多支持猪先飞!

[!--infotagslink--]

相关文章

  • phpems SQL注入(cookies)分析研究

    PHPEMS(PHP Exam Management System)在线模拟考试系统基于PHP+Mysql开发,主要用于搭建模拟考试平台,支持多种题型和展现方式,是国内首款支持题冒题和自动评分与教师评分相...2016-11-25
  • C# WinForm多线程解决界面卡死问题的完美解决方案,使用BeginInvoke

    问题描述:当我们的界面需要在程序运行中不断更新数据时,当一个textbox的数据需要变化时,为了让程序执行中不出现界面卡死的现像,最好的方法就是多线程来解决一个主线程来创建界...2020-06-24
  • ASP/PHP sql注入语句整理大全

    SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作 标准注入语句1.判...2016-11-25
  • PHP防止SQL注入的例子

    防止SQL注入是我们程序开发人员必须要做的事情了,今天我们就来看一篇关于PHP防止SQL注入的例子了,具体的实现防过滤语句可以参考下面来看看吧。 使用prepared以及参...2016-11-25
  • c# 多线程处理多个数据的方法

    这篇文章主要介绍了c# 多线程处理多个数据的方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...2021-03-31
  • C#基于委托实现多线程之间操作的方法

    这篇文章主要介绍了C#基于委托实现多线程之间操作的方法,实例分析了C#的委托机制与多线程交互操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C#多线程中的异常处理操作示例

    这篇文章主要介绍了C#多线程中的异常处理操作,涉及C#多线程及异常的捕获、处理等相关操作技巧,需要的朋友可以参考下...2020-06-25
  • 深入分析C#中的异步和多线程

    这篇文章主要介绍了C#中异步和多线程的相关资料,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...2021-01-16
  • C#多线程与异步的区别详解

    多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别...2020-06-25
  • C#多线程之Thread类详解

    这篇文章主要为大家详细介绍了C#多线程之Thread类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
  • java中多线程与线程池的基本使用方法

    在Java中,我们可以利用多线程来最大化地压榨CPU多核计算的能力,下面这篇文章主要给大家介绍了关于java中多线程与线程池基本使用的相关资料,需要的朋友可以参考下...2021-09-13
  • AngularJS 依赖注入详解及示例代码

    本文主要介绍AngularJS 依赖注入的知识,这里整理了相关的基础知识,并附示例代码和实现效果图,有兴趣的小伙伴可以参考下...2016-08-24
  • PHP中自带函数过滤sql注入代码分析

    SQL注入攻击是黑客攻击网站最常用的手段。如果你的站点没有使用严格的用户输入检验,那么常容易遭到SQL注入攻击。SQL注入攻击通常通过给站点数据库提交不良的数据或...2016-11-25
  • C#中的多线程多参数传递详解

    第一种解决方案的原理是:将线程执行的方法和参数都封装到一个类里面。通过实例化该类,方法就可以调用属性来实现间接的类型安全地传递多个参数...2020-06-25
  • java多线程中执行多个程序的实例分析

    在本篇文章里小编给大家整理的是一篇关于java多线程中执行多个程序的实例分析内容,有需要的朋友们可以学习参考下。...2021-02-07
  • 解析C#多线程编程中异步多线程的实现及线程池的使用

    这篇文章主要介绍了C#多线程编程中异步多线程的实现及线程池的使用,同时对多线程的一般概念及C#中的线程同步并发编程作了讲解,需要的朋友可以参考下...2020-06-25
  • Springboot实现多线程注入bean的工具类操作

    这篇文章主要介绍了Springboot实现多线程注入bean的工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-27
  • c# winform 关闭窗体时同时结束线程实现思路

    th.IsBackground = true解决线程问题,意思就是把线程设置为后台线程,感兴趣的朋友可以多了解下,如何有什么妙招还请多多指导哈...2020-06-25
  • C#多线程编程中的锁系统(三)

    这篇文章主要介绍了C#多线程编程中的锁系统(三),本本文主要说下基于内核模式构造的线程同步方式、事件、信号量以及WaitHandle、AutoResetEvent、ManualResetEvent等内容,需要的朋友可以参考下...2020-06-25
  • Java多线程实现简易微信发红包的方法实例

    这篇文章主要给大家介绍了关于Java多线程实现简易微信发红包的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-01