目录
准备工作
sprinboot整合redis,把集群和发布订阅整合起来
Linux中使用redis(七)SpringBoot整合redis_半截的诗--的博客-CSDN博客
配置过滤器
import com.alibaba.fastjson.JSON;
import com.yka.util.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName:Filter
* @Auther: YooAo
* @Description:
* @Date: 2023/4/10 13:39
* @Version: v1.0
*/
/**
* 检查用户是否已经完成登录
*/
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
@Slf4j
public class FilterTest implements Filter {
// 路径匹配器,
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 1、获取请求的uri
String requestURI = request.getRequestURI();
log.info("拦截到请求:{}", requestURI);
// 定义不需要处理的请求路径
String[] urls = {
"/user/login",//用户登录请求
"/user/register"//用户注册请求
};
// 2、判断本次请求是否需要去处理
boolean check = check(urls, requestURI);
// 3、如果不需要处理则直接放行
if (check) {
log.info("本次请求{}不需要处理", requestURI);
filterChain.doFilter(request,response);
return;
}
// 4-2、判断用户登录状态,如果已登录,则直接放行
if (request.getSession().getAttribute("phone") != null) {
log.info("用户已登录,用户id为:{}", request.getSession().getAttribute("phone"));
String userId= (String) request.getSession().getAttribute("phone");
//把当前登录用户的id设置在ThreadLocal线程里面就可以实现公共字段字段填充了(如,创建时间,更改时间)
//可以明确知道是哪个用户登录了获取到id,随时可以getCurrentId获取到
BaseContext.setCurrentId(userId);
System.out.println("现在线程id为:"+ BaseContext.getCurrentId());
filterChain.doFilter(request, response);
return;
}
// 5、如果未登录,则返回未登录结果
log.error("您没有登录,请先登录,在访问");//设置日志信息
response.getWriter().write(JSON.toJSONString("您没有登录,请先登录,在访问"));//响应
return;
}
/**
* 路径匹配,检查本次请求是否需要放行
*
* @param urls
* @param requestURI
* @return
*/
public boolean check(String[] urls, String requestURI) {
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURI);
if (match) {
return true;
}
}
return false;
}
}
在启动类上添加 @ServletComponentScan 注解
@SpringBootApplication
@ServletComponentScan//开启filter过滤器,添加该注解时@WebFilter注解才会生效
public class SpringbootWeChatRedisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWeChatRedisApplication.class, args);
}
}
配置线程的工具类
/**
* 基于threadLocal封装工具类,用户保存和获取当前用户登录的id
*/
public class BaseContext {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
/**
* 设置当前id
*/
public static void setCurrentId(String id) {
threadLocal.set(id);
}
public static String getCurrentId() {
return threadLocal.get();
}
}
配置响应到前端的工具类
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class R<T> {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
private Map map = new HashMap(); //动态数据
public static <T> R<T> success(T object) {
R<T> r = new R<T>();
r.data = object;
r.code = 1;
return r;
}
public static <T> R<T> error(String msg) {
R r = new R();
r.msg = msg;
r.code = 0;
return r;
}
public R<T> add(String key, Object value) {
this.map.put(key, value);
return this;
}
}
正式开始
创建controller
以下的方法都在这个controller里面,分开讲解了
import com.yka.pojo.R;
import com.yka.util.BaseContext;
import com.yka.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
* @ClassName:FriendController
* @Auther: YooAo
* @Description: 添加微信好友
* @Date: 2023/5/9 14:19
* @Version: v1.0
*/
@RestController
@RequestMapping("/friend")
public class FriendController {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedisUtil redisUtil;
}
1、发送添加通知
/**
* 1、给要添加的好友发送好友通知,给订阅通知发送消息
* @param friendPhone 好友手机号
*/
@GetMapping("/sendInform")
public R<String> sendInform(String friendPhone){
//获取当前登录的手机号
String phone = BaseContext.getCurrentId();
//判断该用户在微信里是否存在
Map<Object, Object> hmget = redisUtil.hmget("WeChat:user:" + friendPhone);
if(hmget.isEmpty()){
return R.success("该用户不存在");
}
//不能添加自己
if(phone.equals(friendPhone)){
return R.success("好友为当前用户,不能添加,不要瞎搞");
}
//判断添加的好友在当前用户的好友列表里是否存在,存在就不能添加了
Set range = redisTemplate.opsForZSet().range("WeChat:userList:" + phone + ":friends:", 0, -1);
for (Object o : range) {
//如果这个集合里有该账号,就不能在添加了
if(o.equals(friendPhone)){
return R.error("好友已存在,添加失败");
}
}
//把当前用户的手机号和当前用户要添加的好友的手机号发送到订阅通道中
String mes = phone + "——" + friendPhone;
//给指定订阅发送消息,底层是发布消息publish(rawChannel, rawMessage),给指定订阅发布消息
//订阅发布消息就会调用RedisMessageListener这个类的onMessage方法参数也会传过去:接收订阅消息监听器就会接收到
//因为RedisMessageListener它是消息监听器,所有订阅通知发送的消息RedisMessageListener它都会接收到
redisUtil.convertAndSend("addFriend",mes);
return R.success("发送通知成功");
}
1.1消息接收监听器
准备工作应该已经配置好了,现在要改善代码
import com.yka.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;
/**
* Redis 消息接收 监听器,接收订阅通道的消息,发布的订阅消息都会被这里接收
*
* @Author Lizhou
**/
@Slf4j
@Component
public class RedisMessageListener implements MessageListener {
@Autowired
private RedisUtil redisUtil;
/**
*
* @param message 订阅通道发布的信息
* @param pattern 通道名称
*/
@Override
public void onMessage(Message message, byte[] pattern) {
// 接收的topic channel:通道
log.info("channel:" + new String(pattern));
// 消息的POJO message:信息
log.info("message:" + message.toString());
//接收的消息 String message = phone + "——" + friendPhone;
//message.toString() = "phone —— friendPhone",要把双引号和——去掉
String substring = message.toString().substring(1, message.toString().length() - 1);//把双引号去掉
String[] split = substring.split("——");
System.out.println(split[0]);
System.out.println(split[1]);
//把好友的手机号为键,当前用户为value
//比如:199是当前用户(value),188是要添加的好友(键),
//199向188发送好友请求,这时要登录188的用户进行操作要同意还是拒绝
//因为188它是键,所以要通过键获取value就能获取到谁要添加188为好友的用户了就是199用户
redisUtil.lSet("WeChat:friendInformList:"+split[1],split[0]);//存放到list集合中
}
}
1.2测试
前端发送请求
当前登录的是 15076123618
要添加12345678为好友发送申请
redis数据库查看list集合,通知列表
2、查看当前用户的好友通知列表
/**
* 2、查看当前用户好友通知列表,看看有没有好友加我,记得要切换用户查询
* @return
*/
@GetMapping("/friendInformList")
public R<List> friendInformList(){
//如果199给188发送添加邀请,这里要切换到188用户查看199用户发来的添加申请
String phone = BaseContext.getCurrentId();
//获取当前用户的好友通知列表,看看都有谁加我了,详细看RedisMessageListener这个类设置了list集合
List<Object> objects = redisUtil.lGet("WeChat:friendInformList:"+phone, 0, -1);
//把相同的好友发来的申请去重了
HashSet<Object> set = new HashSet<>(objects);
List<Object> objects2 = new ArrayList<>(set);
return R.success(objects2);
}
2.1测试
前端发送请求
切换用户12345678
查看好友申请列表
3、添加好友
注意:只能在添加好友通知列表中看到的好友申请才能操作,是否添加微信好友,先查询好友通知列表,有好友申请才同意还是拒绝
/**
* 3、这个接口是只有在好友通知列表中显示的好友才能添加,是否要添加微信好友
* 3、只能在添加好友通知列表中看到的好友申请才能操作,是否添加微信好友,先查询好友通知列表,有好友申请才同意还是拒绝
* @param friendPhone 好友的手机号
* @param state 状态,1为同意添加,0为拒绝添加
* @return
*/
@PostMapping("/addFriend")
public R<String> addFriend(String friendPhone,Integer state){
if(state==1){
//获取当前登录的手机号
String phone = BaseContext.getCurrentId();
//添加好友
Boolean b1 = redisTemplate.opsForZSet().add("WeChat:userList:" + phone + ":friends:", friendPhone, new Date().getTime());
Boolean b2 = redisTemplate.opsForZSet().add("WeChat:userList:" + friendPhone + ":friends:", phone, new Date().getTime());
//添加好友之后把好友通知列表移除掉 count = 0 : 移除表中所有与 VALUE 相等的值。lrem list 0 a
redisUtil.lRemove("WeChat:friendInformList:"+phone,0,friendPhone);
if(b1==false || b2==false){
return R.success("添加失败,该好友已存在");
}
return R.success("添加成功");
}
if (state==0){
return R.error("拒绝添加好友");
}
return R.error("没有添加成功");
}
3.1测试
收到好友申请后就可以添加好友了,1同意,0拒绝
redis数据库显示12345678用户的好友有15076123618用户了,所以添加成功了
4、查看当前用户的好友列表
/**
* 4、获取好友列表
* @return
*/
@GetMapping("/getFriends")
public R<List<Object>> getFriends() {
//获取当前登录的手机号
String phone = BaseContext.getCurrentId();
//获取当前用户的好友列表,但这个集合里只有手机号,所以要通过手机号唯一标识去查询该好友的信息
Set range = redisTemplate.opsForZSet().range("WeChat:userList:" + phone + ":friends:", 0, -1);
//把循环查询出来的用户信息存放在list集合里面
ArrayList<Object> list = new ArrayList<>();
//循环好友列表,通过手机号唯一标识去查询该用户的信息
for (Object object : range) {
//获取当前用户好友的信息
Map<Object, Object> hmget = redisUtil.hmget("WeChat:user:" + object);
hmget.remove("password");//不显示密码
list.add(hmget);//把循环查询出来的用户信息存放在list集合里面
}
return R.success(list);
}
4.1测试
完事,搞定