本文目录

一、背景描述

二、问题原因

原因一:使用 @Resource 注解导致的问题

原因二:粗心导致

原因三:注解配置缺少属性

三、解决方案

方案一:自动注入使用 @Autoware 注解

方案二:仔细检查配置文件

方案三:注解里添加必要的属性


 

一、背景描述

项目技术栈: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 

完结!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐