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