记@ComponentScan注解的坑

简介: 记@ComponentScan注解的坑

因为项目要对接小程序,因此引入了小程序依赖

        <weixin-java-miniapp.version>4.4.0</weixin-java-miniapp.version>

        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-miniapp</artifactId>
            <version>${weixin-java-miniapp.version}</version>
        </dependency>

配置类


/**
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(WxMaProperties.class)
public class WxMaConfiguration {
    private final WxMaProperties properties;

    @Autowired
    public WxMaConfiguration(WxMaProperties properties) {
        this.properties = properties;
    }

    @Bean
    public WxMaService wxMaService() {
        List<WxMaProperties.Config> configs = this.properties.getConfigs();
        if (configs == null) {
            throw new WxRuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
        }
        WxMaService maService = new WxMaServiceImpl();
        maService.setMultiConfigs(
            configs.stream()
                .map(a -> {
                    WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
//                WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool());
                    // 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常
                    config.setAppid(a.getAppid());
                    config.setSecret(a.getSecret());
                    config.setToken(a.getToken());
                    config.setAesKey(a.getAesKey());
                    config.setMsgDataFormat(a.getMsgDataFormat());
                    return config;
                }).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o)));
        return maService;
    }

    @Bean
    public WxMaMessageRouter wxMaMessageRouter(WxMaService wxMaService) {
        final WxMaMessageRouter router = new WxMaMessageRouter(wxMaService);
        router
            .rule().handler(logHandler).next()
            .rule().async(false).content("订阅消息").handler(subscribeMsgHandler).end()
            .rule().async(false).content("文本").handler(textHandler).end()
            .rule().async(false).content("图片").handler(picHandler).end()
            .rule().async(false).content("二维码").handler(qrcodeHandler).end();
        return router;
    }

    private final WxMaMessageHandler subscribeMsgHandler = (wxMessage, context, service, sessionManager) -> {
        service.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder()
            .templateId("此处更换为自己的模板id")
            .data(Lists.newArrayList(
                new WxMaSubscribeMessage.MsgData("keyword1", "339208499")))
            .toUser(wxMessage.getFromUser())
            .build());
        return null;
    };

    private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
        log.info("收到消息:" + wxMessage.toString());
        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
            .toUser(wxMessage.getFromUser()).build());
        return null;
    };

    private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
            .toUser(wxMessage.getFromUser()).build());
        return null;
    };

    private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
        try {
            WxMediaUploadResult uploadResult = service.getMediaService()
                .uploadMedia("image", "png",
                    ClassLoader.getSystemResourceAsStream("tmp.png"));
            service.getMsgService().sendKefuMsg(
                WxMaKefuMessage
                    .newImageBuilder()
                    .mediaId(uploadResult.getMediaId())
                    .toUser(wxMessage.getFromUser())
                    .build());
        } catch (WxErrorException e) {
            e.printStackTrace();
        }

        return null;
    };

    private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
        try {
            final File file = service.getQrcodeService().createQrcode("123", 430);
            WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
            service.getMsgService().sendKefuMsg(
                WxMaKefuMessage
                    .newImageBuilder()
                    .mediaId(uploadResult.getMediaId())
                    .toUser(wxMessage.getFromUser())
                    .build());
        } catch (WxErrorException e) {
            e.printStackTrace();
        }

        return null;
    };

}



/**
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {

    private List<Config> configs;

    @Data
    public static class Config {
        /**
         * 设置微信小程序的appid
         */
        private String appid;

        /**
         * 设置微信小程序的Secret
         */
        private String secret;

        /**
         * 设置微信小程序消息服务器配置的token
         */
        private String token;

        /**
         * 设置微信小程序消息服务器配置的EncodingAESKey
         */
        private String aesKey;

        /**
         * 消息格式,XML或者JSON
         */
        private String msgDataFormat;
    }

}

controller:


/**
 * 微信小程序用户接口
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@RestController
@AllArgsConstructor
@Slf4j
@RequestMapping("/wx/user/{appid}")
public class WxMaUserController {
    private final WxMaService wxMaService;

    /**
     * 登陆接口
     */
    @GetMapping("/login")
    public String login(@PathVariable String appid, String code) {
        if (StringUtils.isBlank(code)) {
            return "empty jscode";
        }

        if (!wxMaService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
        }

        try {
            WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
            log.info(session.getSessionKey());
            log.info(session.getOpenid());
            //TODO 可以增加自己的逻辑,关联业务相关数据
            return JsonUtils.toJson(session);
        } catch (WxErrorException e) {
            log.error(e.getMessage(), e);
            return e.toString();
        } finally {
            WxMaConfigHolder.remove();//清理ThreadLocal
        }
    }

    /**
     * <pre>
     * 获取用户信息接口
     * </pre>
     */
    @GetMapping("/info")
    public String info(@PathVariable String appid, String sessionKey,
                       String signature, String rawData, String encryptedData, String iv) {
        if (!wxMaService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
        }

        // 用户信息校验
        if (!wxMaService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
            WxMaConfigHolder.remove();//清理ThreadLocal
            return "user check failed";
        }

        // 解密用户信息
        WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionKey, encryptedData, iv);
        WxMaConfigHolder.remove();//清理ThreadLocal
        return JsonUtils.toJson(userInfo);
    }

    /**
     * <pre>
     * 获取用户绑定手机号信息
     * </pre>
     */
    @GetMapping("/phone")
    public String phone(@PathVariable String appid, String sessionKey, String signature,
                        String rawData, String encryptedData, String iv) {
        if (!wxMaService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
        }

        // 用户信息校验
        if (!wxMaService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
            WxMaConfigHolder.remove();//清理ThreadLocal
            return "user check failed";
        }

        // 解密
        WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
        WxMaConfigHolder.remove();//清理ThreadLocal
        return JsonUtils.toJson(phoneNoInfo);
    }

}

问题来了: 项目死活起不起来。排查了一下午,后面找到问题了:

我们springboot启动类配置了@ComponentScan 注解。

@ComponentScan:
作用
主要是从定义的扫描路径中,找出标识了需要装配的类自动装配到Spring的bean容器中。

简单的说就是 @ComponentScan告诉Spring从哪里找到bean,一旦指定了,Spring就会将指定的包及其下级的包中寻找bean。

在SpringBoot项目中,我们并没有显示的看到该注解,但是仍然能扫描到bean呢?

其实,在创建SpringBoot项目中,默认在启动类上添加了@SpringBootApplication注解,该注解中包含@ComponentScan注解,因此SpringBoot会自动帮我们扫描启动类所在的包。

然后我的properties配置类没有被@ComponentScan注解扫描到,所以就报错了。。。
我本来还看了一眼,发现我的配置类在主启动类包下,但后面发现竟然扫描不进去IOC容器!然后还以为我的接口和配置类有问题,搞了一下午,后面看到主启动类上有个@ComponentScan注解!心态崩了!
扫描不进去的原因:
本来如果不配置@ComponentScan注解,主启动类会把它所在的包下的所有bean扫描进IOC容器。但是一旦主启动类上加了@ComponentScan注解,它就只会扫描@ComponentScan注解里面所写的路径!

害!

目录
相关文章
|
运维 Ubuntu Linux
【服务器】安装Docker环境
【服务器】安装Docker环境
711 0
|
10天前
|
存储 人工智能 搜索推荐
Mem0 + Milvus:为人工智能构建持久化长时记忆
Mem0 为AI打造持久记忆层,结合Milvus向量数据库,让智能体记住用户偏好、追溯历史对话,实现个性化持续交互,告别“健忘”AI。
Mem0 + Milvus:为人工智能构建持久化长时记忆
|
C++
VS Code识别编辑规范,ESlint规则,VS Code保存去掉自动加分号、逗号、双引号
书写规范是非常重要的,除了统一,还要简洁。 1、VS Code识别编辑规范 在项目根目录新建.editorconfig文件,加入设置:
1133 0
|
10月前
|
小程序 前端开发 算法
|
7月前
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
350 0
|
9月前
|
消息中间件 存储 监控
高弹性、低成本的云消息队列RabbitMQ 版
本次课程由阿里云消息队列产品专家杨文婷分享,主题为高弹性、低成本的云消息队列RabbitMQ。内容涵盖四个方面:1) 产品优势,包括兼容开源客户端、解决稳定性痛点和高弹性低成本;2) 架构实现原理,如分布式架构和弹性调度系统;3) Serverless系列带来的按量付费模式和资源池优势;4) Serverless适用场景,如开发测试环境、峰谷流量业务等。最后解答了关于顺序消费、与普通MQ对比、自动扩容及API支持等常见问题。
356 46
|
存储 缓存 算法
一篇文章让你学会什么是哈希(上)
哈希概念 哈希在C++中有广泛的应用,它是一种用于快速查找和存储数据的数据结构和算法。以下是一些常见的哈希在C++中的应用: 哈希表(Hash Table):哈希表是一种高效的数据结构,用于存储键值对。在C++中,std::unordered_map 和 std::unordered_set 是标准库提供的哈希表实现。
|
设计模式 测试技术 数据处理
|
算法 Python
Python函数的嵌套调用:深入理解与应用
Python函数的嵌套调用:深入理解与应用
343 1
|
Java Spring 容器
springboot如何忽略扫描某个包范围的组件
springboot如何忽略扫描某个包范围的组件