springboot redis 发布订阅 注解实现及使用以及键过期事件

    在spring boot 中发布订阅的使用需要自己去实现MessageListener然后配置放入RedisMessageListenerContainer当中,觉得配置有些繁琐用起来不是那么方便,于是就使用idea打开RedisMessageListenerContainer源代码查看,打开的第一眼就发现他是通过SmartLifecycle启动监听的,看过源码或者百度过的人都知道这个是在spring bean 初始化完成之后finishRefresh中才会调用到的一个接口,所以后面处理的思路就简单了,主要是使用BeanPostProcessor实现,上代码:

一、首先自定一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface RedisSubscribe {

    /**
     * 主题
     */
    String [] value();

}
三、增加一个封装监听信息的类
@Data
public class RedisListenInfo {

    private List<Topic> topic;
    private Object object;
    private Method method;
    private List<Class<?>> parameterTypes;

}
三、增加一个动态代理类
public class MessageListenInvocationHandler implements InvocationHandler {

    private RedisListenInfo redisListenInfo;

    public MessageListenInvocationHandler(RedisListenInfo redisListenInfo) {
        this.redisListenInfo = redisListenInfo;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!method.getName().equals("onMessage")) {
            // 由于 RedisMessageListenerContainer 在存MessageListener的时候直接用对象,容器是HashMap所以会调用hashCode
            if (method.getName().equals("hashCode")) {
                return hashCode();
            } else {
                return 1;
            }
        }
        List<Class<?>> parameterTypes = redisListenInfo.getParameterTypes();
        Method targetMethod = redisListenInfo.getMethod();
        if (parameterTypes == null || parameterTypes.isEmpty()) {
            return targetMethod.invoke(redisListenInfo.getObject());
        } else {
            Object[] params = new Object[parameterTypes.size()];
            for (int i = 0; i < parameterTypes.size(); i++) {
                Class<?> aClass = parameterTypes.get(i);
                params[i] = getMethodParam(aClass, args);
            }
            return targetMethod.invoke(redisListenInfo.getObject(), params);
        }
    }

    public Object getMethodParam(Class<?> paramType, Object[] args) {
        if (paramType.isAssignableFrom(String.class)) {
            return args[0].toString();
        } else if (paramType.isAssignableFrom(Message.class)) {
            return args[0];
        } else if (paramType.isAssignableFrom(byte[].class)) {
            return args[1];
        } else if (paramType.isAssignableFrom(Topic.class)) {
            return new ChannelTopic(new String((byte[]) args[1]));
        } else {
            return null;
        }
    }
}
四、增加一个配置类,需要实现BeanPostProcessor
/**
 * Redis 发布订阅自动配置
 */
@Configuration
public class RedisListenAutoConfig implements BeanPostProcessor {
	
	private static final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
	
	@Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        container.setConnectionFactory(connectionFactory);
        return container;
    }
	
	@Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 扫描订阅
        scannerRedisSubscribe(bean);
        return bean;
    }
    
    private void scannerRedisSubscribe(Object bean){
	    List<RedisListenInfo> redisListenInfoList = getRedisListenInfo(bean);
	    if(redisListenInfoList != null){
	        addRedisListener(redisListenInfoList);
	    }
    }
    
    // 获取Redis订阅所需要的用到的信息,【对象,方法,订阅主题】
	public List<RedisListenInfo> getRedisListenInfo(Object obj){
	        Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(obj.getClass());
	        return Arrays.stream(allDeclaredMethods)
	                .filter(m -> m.isAnnotationPresent(RedisSubscribe.class)).map(m -> {
	                    RedisSubscribe annotation = m.getAnnotation(RedisSubscribe.class);
	                    RedisListenInfo redisListenInfo = new RedisListenInfo();
	                    redisListenInfo.setObject(obj);
	                    redisListenInfo.setTopic(Arrays.stream(annotation.value()).map(ChannelTopic::new).collect(Collectors.toList()));
	                    redisListenInfo.setMethod(m);
	                    redisListenInfo.setParameterTypes(Arrays.asList(m.getParameterTypes()));
	                    return redisListenInfo;
	                }).collect(Collectors.toList());
	    }
	}
    // 通过jdk动态代理实现MessageListener
    private void addRedisListener(List<RedisListenInfo> redisListenInfo){
        redisListenInfo.forEach(d->{
            MessageListener messageListener = (MessageListener)Proxy.newProxyInstance(
                    RedisListenAutoConfig.class.getClassLoader(),
                    new Class[]{MessageListener.class},
                    new MessageListenInvocationHandler(d));
            container.addMessageListener(messageListener,d.getTopic());
        });
    }
五、使用方式
@Service
public class ServiceImpl {
	
	@RedisSubscribe("topic")
	public void topicSubscribe(Message message,byte [] bytes){
	   // Business logic
    }

}

@RequestMapping("open")
@RestController
public class Publish{
	private StringRedisTemplate stringRedisTemplate;
	
	@RequestMapping("publish")
	public Object publish(){
	    stringRedisTemplate.convertAndSend("topic","消息内容");
	}
	
}

文章到此也就结束了,至于键过期事件,配置好KeyExpirationEventMessageListener,他会发布spring的监听事件RedisKeyExpiredEvent,如果想自定义实现,可以仿造这个类进行扩展,思路和上面的发布订阅的实现差不多,没有写得很仔细,觉得有问题的地方希望各位大佬不吝赐教.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值