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

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的解决方案!分享完毕了,快去试试吧!希望大家多多支持我们!

(0)

相关推荐

  • 项目依赖Springboot jar失败解决方案

    1.原因 因为springboot-maven-plugin打包的第一级目录为Boot-INF,无法引用 2.解决 不能使用springboot项目自带的打包插件进行打包 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </p

  • 详解Springboot对多线程的支持

    这两天看阿里的JAVA开发手册,到多线程的时候说永远不要用 new Thread()这种方式来使用多线程.确实是这样的,我一直在用线程池,到了springboot才发现他已经给我们提供了很方便的线程池机制. 本博客代码托管在github上https://github.com/gxz0422042... 一.介绍 Spring是通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用ThreadPoolTaskExecutor来创建一个基于线城池的TaskExecutor.在使用线程

  • 解决Springboot @Autowired 无法注入问题

    特别提醒:一定要注意文件结构 WebappApplication 一定要在包的最外层,否则Spring无法对所有的类进行托管,会造成@Autowired 无法注入. 1. 添加工具类获取在 Spring 中托管的 Bean (1)工具类 package com.common; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionE

  • Spring boot多线程配置方法

    本文实例为大家分享了Spring boot多线程配置的具体代码,供大家参考,具体内容如下 1.配置线程配置类 package test; import java.util.concurrent.Executor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.ComponentScan; import o

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

    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.call

  • 解决Springboot2.1.x配置Activiti7单独数据源问题

    1|1简介 最近基于最新的Activiti7配置了SpringBoot2. 简单上手使用了一番.发现市面上解决Activiti7的教程很少,采坑也比较多,在Activiti6配置数据源和Activiti7有所区别,基于Activiti6在Activiti7里是无法正常使用的.接下来让我们看下区别. 1|2问题 Activiti6多数据源配置 6的配置比较简单点. 先加入配置: # activiti 数据源 spring.datasource.activiti.driver=com.mysql.j

  • Spring 多线程下注入bean问题详解

    本文介绍了Spring 多线程下注入bean问题详解,分享给大家,具体如下: 问题 Spring中多线程注入userThreadService注不进去,显示userThreadService为null异常 代码如下: public class UserThreadTask implements Runnable { @Autowired private UserThreadService userThreadService; @Override public void run() { AdeUs

  • 解决spring-boot2.0.6中webflux无法获得请求IP的问题

    这几天在用 spring-boot 2 的 webflux 重构一个工程,写到了一个需要获得客户端请求 IP 的地方,发现写不下去了,在如下的 Handler(webflux 中 Handler 相当于 mvc 中的 Controller)中 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; im

  • 解决python多线程报错:AttributeError: Can't pickle local object问题

    报错信息: Traceback (most recent call last): File "D:/flaskProject/test.py", line 35, in test pool.apply(self.out, args=(i,)) File "Python37-32\lib\multiprocessing\pool.py", line 261, in apply return self.apply_async(func, args, kwds).get(

  • 解决springboot 多线程使用MultipartFile读取excel文件内容报错问题

    springboot项目开启多线程 启动类加注解开启 @EnableAsync,实现类方法加注解 @Async 前端页面 报错信息 java.io.FileNotFoundException: C:\Users\dongao\AppData\Local\Temp\tomcat.1255209411477782290.8051\work\Tomcat\localhost\ROOT\upload_7d7b99e5_38da_4a03_93e0_bff20cb48022_00000000.tmp (系

  • JAVA解决在@autowired,@Resource注入为null的情况

    使用SpringMVC或者SSH过程中,有时可能会遇到这么一个问题.就是在一个普通的JAVA类(不是controller也不是action类)中无法注入在spring配置文件中配置的bean. 比如你在一个普通java类想调用某个在spring中配置的service,你会发现不管你用@Resource还是@Autowired注解都无法注入,对象始终是null. 那是因为一般普通的Java类没有被spring代理,自然无法通过spring注入相关的对象.难道这样就不能调用了吗?这里提供下面一个类来

  • 如何解决Java多线程死锁问题

    死锁问题 死锁定义 多线程编程中,因为抢占资源造成了线程无限等待的情况,此情况称为死锁. 死锁举例 注意:线程和锁的关系是:一个线程可以拥有多把锁,一个锁只能被一个线程拥有. 当两个线程分别拥有一把各自的锁之后,又尝试去获取对方的锁,这样就会导致死锁情况的发生,具体先看下面代码: /** * 线程死锁问题 */ public class DeadLock { public static void main(String[] args) { //创建两个锁对象 Object lock1 = new

  • spring boot使用@Async注解解决异步多线程入库的问题

    目录 前言 项目实况介绍 第一种方式 第二种方式 这里有个坑! 这里有两个坑! 总结 前言 在开发过程中,我们会遇到很多使用线程池的业务场景,例如定时任务使用的就是ScheduledThreadPoolExecutor.而有些时候使用线程池的场景就是会将一些可以进行异步操作的业务放在线程池中去完成,例如在生成订单的时候给用户发送短信,生成订单的结果不应该被发送短信的成功与否所左右,也就是说生成订单这个主操作是不依赖于发送短信这个操作,所以我们就可以把发送短信这个操作置为异步操作.而要想完成异步操

随机推荐