解决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的解决方案!分享完毕了,快去试试吧!希望大家多多支持猪先飞!
相关文章
- PHPEMS(PHP Exam Management System)在线模拟考试系统基于PHP+Mysql开发,主要用于搭建模拟考试平台,支持多种题型和展现方式,是国内首款支持题冒题和自动评分与教师评分相...2016-11-25
C# WinForm多线程解决界面卡死问题的完美解决方案,使用BeginInvoke
问题描述:当我们的界面需要在程序运行中不断更新数据时,当一个textbox的数据需要变化时,为了让程序执行中不出现界面卡死的现像,最好的方法就是多线程来解决一个主线程来创建界...2020-06-24- SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作 标准注入语句1.判...2016-11-25
- 防止SQL注入是我们程序开发人员必须要做的事情了,今天我们就来看一篇关于PHP防止SQL注入的例子了,具体的实现防过滤语句可以参考下面来看看吧。 使用prepared以及参...2016-11-25
- 这篇文章主要介绍了c# 多线程处理多个数据的方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...2021-03-31
- 这篇文章主要介绍了C#基于委托实现多线程之间操作的方法,实例分析了C#的委托机制与多线程交互操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了C#多线程中的异常处理操作,涉及C#多线程及异常的捕获、处理等相关操作技巧,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了C#中异步和多线程的相关资料,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...2021-01-16
- 多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别...2020-06-25
- 这篇文章主要为大家详细介绍了C#多线程之Thread类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
- 在Java中,我们可以利用多线程来最大化地压榨CPU多核计算的能力,下面这篇文章主要给大家介绍了关于java中多线程与线程池基本使用的相关资料,需要的朋友可以参考下...2021-09-13
- 本文主要介绍AngularJS 依赖注入的知识,这里整理了相关的基础知识,并附示例代码和实现效果图,有兴趣的小伙伴可以参考下...2016-08-24
- SQL注入攻击是黑客攻击网站最常用的手段。如果你的站点没有使用严格的用户输入检验,那么常容易遭到SQL注入攻击。SQL注入攻击通常通过给站点数据库提交不良的数据或...2016-11-25
- 第一种解决方案的原理是:将线程执行的方法和参数都封装到一个类里面。通过实例化该类,方法就可以调用属性来实现间接的类型安全地传递多个参数...2020-06-25
- 在本篇文章里小编给大家整理的是一篇关于java多线程中执行多个程序的实例分析内容,有需要的朋友们可以学习参考下。...2021-02-07
- 这篇文章主要介绍了C#多线程编程中异步多线程的实现及线程池的使用,同时对多线程的一般概念及C#中的线程同步并发编程作了讲解,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了Springboot实现多线程注入bean的工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-27
- th.IsBackground = true解决线程问题,意思就是把线程设置为后台线程,感兴趣的朋友可以多了解下,如何有什么妙招还请多多指导哈...2020-06-25
- 这篇文章主要介绍了C#多线程编程中的锁系统(三),本本文主要说下基于内核模式构造的线程同步方式、事件、信号量以及WaitHandle、AutoResetEvent、ManualResetEvent等内容,需要的朋友可以参考下...2020-06-25
- 这篇文章主要给大家介绍了关于Java多线程实现简易微信发红包的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-01