一、背景描述
项目技术栈:Spring boot (2.1.5.RELEASE) + mqtt (5.1.5.RELEASE)
项目是一个 Springboot 项目,集成了 EMQX,项目在启动时,提示应用启动失败,详情如下:
*************************** APPLICATION FAILED TO START *************************** Description: The bean 'mqttSender' could not be injected as a 'com.iot.back.net.device.infrastructure.component.MqttMessageComponent' because it is a JDK dynamic proxy that implements: Action: Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.
详细信息如图所示:
二、问题原因
原因一:使用 @Resource 注解导致的问题
因为 @Autowired 默认按类型装配,而 @Resource 优先按名称装配。
比如项目中存在一个 RedisTemplate 的 bean,而由于需求的原因,又自定义了一个 RedisTemplate (比如切换 Redis 的数据库时),然后如果使用 @Resource 注解,由于项目中已经有另外一个 bean 叫 “RedisTemplate”,也可能出现这个错误。
原因二:粗心导致
比如基于 xml 的配置
<bean id="powerSwtichService" class="com.xxx.xxx.xxx.xx.PowerSwtichService"/>
这里的 class 并不是实际应该配置的:com.xxx.xxx.xxx.xx.PowerSwtichService
原因三:注解配置缺少属性
@EnableAsync、@EnableCaching 或者 @EnableAspectJAutoProxy 再或者 @EnableTransactionManagement 这些注解都可以设置 proxyTargetClass = true 属性
主要配置基于 JDK 的代理还是基于类的动态代理的配置,这种错误提示需要设置基于类的代理才行。
三、解决方案
方案一:自动注入使用 @Autoware 注解
此方案主要是针对原因一导致的问题而使用的解决方法,我项目里的解决方法就是使用方案一搞定的。
@Slf4j @Component public class MusicQueryStateManager { @Autowired private MqttMessageComponent mqttMessageComponent; @Resource private HomeRpc homeRpc; @Resource private RedisTemplate<String, String> redisTemplateDb16; public void handleDeviceAction(CommonControlParam controlParam) { log.info("E|MusicQueryStateManager|handleDeviceAction()|处理设备动作控制查询状态!controlParam = {}", JSONUtil.toJsonStr(controlParam)); String hostSn = homeRpc.getHostSnByHomeSn(controlParam.getSn()); String key = RedisKeyConstants.DEVICE_STATE + controlParam.getSn() + StrUtil.COLON + controlParam.getDeviceId(); String value = redisTemplateDb16.opsForValue().get(key); DeviceStateDTO deviceStateDTO = JSONObject.parseObject(value, DeviceStateDTO.class); Assert.notNull(deviceStateDTO, "deviceStateDTO不能为空!"); sendMessageToOpenPlatform(controlParam, hostSn, deviceStateDTO.getProperties()); } /** * 发送消息给开放平台 * * @param controlParam 控制请求参数 * @param hostSn 主机sn */ private void sendMessageToOpenPlatform(CommonControlParam controlParam, String hostSn, JSONObject properties) { String uuid = controlParam.getUuid(); String topic = "thirdBgmusic/tgw_cloud/control/" + uuid + "/queryState"; JSONObject data = new JSONObject(); data.put("channel", 1); data.put("properties", properties); MqttMessagePublisher mqttPublisher = new MqttMessagePublisher.Builder() .title("背景音乐设备动作控制播放状态消息发送!") .topic(topic) .identity(hostSn) .clientType("tgw_host") .msgId(controlParam.getMsgId()) .compression(CompressionEnum.ZLIB.getType()) .encry(false) .data(data) .params(Params.create().set("sn", hostSn)) .build(); mqttMessageComponent.sendMessage(mqttPublisher); } }
方案二:仔细检查配置文件
比如 xml 配置文件,properties 配置文件,yml 配置文件等。
方案三:注解里添加必要的属性
比如:
- @EnableAsync(proxyTargetClass = true) 或者
- @EnableCaching(proxyTargetClass = true) 或者
- @EnableAspectJAutoProxy(proxyTargetClass = true) 或者
- @EnableTransactionManagement(proxyTargetClass = true)
proxy-target-class 属性值决定是基于 JDK 接口还是基于类的代理被创建。
- 如果为 true 代表基于类的代理,
- 如果为 false 代表基于 JDK 接口的代理。
如果 springboot 项目,也可以在配置文件里写上如下内容:
spring.aop.proxy-target-class=true
完结!