因为项目要对接小程序,因此引入了小程序依赖
<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注解里面所写的路径!
害!