栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

对spring-data-redis方法增强的方案,不修改源码实现简单接入

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

对spring-data-redis方法增强的方案,不修改源码实现简单接入

背景/需求

​ 接入需要对spring-data-redis的使用进行改造,在其操作redis的不同方法调用前,加入不同的逻辑。

方案一

​ 针对于大多数系统中引入的spring-data-redis来说,对于redis中K-V类型的操作由RedisTemplate、ValueOperations进行。所以下载spring-data-redis源码,对目标类的相关方法进行侵入式改造。

方案二

​ 思考:有没有不修改源码的方法?

​ 修改源码的方式对于业务系统来说,每次升级spring-data-redis都需要加入相同的逻辑,再重新发版。尝试观察后发现,RedisTemplate是public的,ValueOperations也是public的,是否可以通过设计模式的手段实现。

​ 我们要做到对RedisTemplate的delete方法加入一些逻辑,继承重写它不就行了?这样在代码中配置RedisTemplate的时候只需要修改RedisTemplate的引用为我们新建的子类,就可以满足既有原逻辑,又可以加入新逻辑。(这样破坏了里氏代换原则)

public class MyRedisTemplate extends RedisTemplate {
    @Override
    public void delete(K key) {
        super.delete(key);
        HotkeyStoreExecutor.remove(key);
    }

    @Override
    public void delete(Collection keys) {
        super.delete(keys);
        HotkeyStoreExecutor.remove(keys);
    }
}

​ 重写完delete方法后,还有其他一些操作呢,如何处理?查看代码发现,我去,都是私有属性,那有没有公共的调用方法呢。

 */
public class RedisTemplate extends RedisAccessor implements RedisOperations, BeanClassLoaderAware {
	...
// cache singleton objects (where possible)
private ValueOperations valueOps;
private ListOperations listOps;
private SetOperations setOps;
private ZSetOperations zSetOps;
private GeoOperations geoOps;
private HyperLogLogOperations hllOps;
  ... 
}

​ 通过idea查看到,valueOps对外暴露是通过opsForValue(),首次调用时进行初始化操作,返回ValueOperations接口的实现类DefaultValueOperations。listOps、setOps、zSetOps、geoOps、hllOps同理。

public ValueOperations opsForValue() {
   if (valueOps == null) {
      valueOps = new DefaultValueOperations(this);
   }
   return valueOps;
}

​ 对于接口,怎样才能对DefaultValueOperations进行一个不同方法的不同逻辑的改造呢?DefaultValueOperations是ValueOperations的实现类,这个场景刚好适合静态代理模式:所谓静态代理也就是在程序运行前就已经存在代理类的字节码文件,实现方法如下面的类图,

​ 如果一个目标类实现自接口,那么他的代理类同样实现接口,引入目标类的实例对象作为属性,通过目标类实现所有方法,但是真正调用的实例类是代理类。

​ DefaultValueOperationsProxy实现ValueOperrations,作为DefaultValueOperations的代理类,引入DefaultValueOperations作为私有属性,重写的方法使用DefaultValueOperations做原方法调用,也可以加入新逻辑。

public class DefaultValueOperationsProxy implements ValueOperations {
  //此处属性是DefaultValueOperations的引用
    ValueOperations valueOperations;
	//构造方法
    public DefaultValueOperationsProxy(ValueOperations valueOperations) {
        this.valueOperations = valueOperations;
    }

    @Override
    public void set(K key, V value) {
        valueOperations.set(key, value);
     //TODO 自己的逻辑
    }
...
    @Override
    public V get(Object key) {
  		//TODO 自己的逻辑
        return  valueOperations.get(key);
    }

...
    @Override
    public Integer append(K key, String value) {
      //TODO 自己的逻辑
        return valueOperations.append(key, value);
    }
...
    @Override
    public Long size(K key) {
  		//TODO 自己的逻辑
        return valueOperations.size(key);
    }

    @Override
    public RedisOperations getOperations() {
      //TODO 自己的逻辑
        return valueOperations.getOperations();
    }
}

接下来就是使用DefaultValueOperations的代理类,重写OpsForValue()方法,在第一次调用时,调用原opsForValue() 方法获取实例化的DefaultValueOperations,返回其代理实例。

public class HotKeyRedisTemplate extends RedisTemplate {
    //初始化时为空
    ValueOperations valueOperationsProxy;

    @Override
    @SuppressWarnings("unchecked")
    public ValueOperations opsForValue() {

        //初次执行初始化
        if (valueOperationsProxy == null) {
            //原opsForValue() 方法
            ValueOperations valueOperations = super.opsForValue();
            valueOperationsProxy = new DefaultValueOperationsProxy(valueOperations);
        }
        return valueOperationsProxy;
    }
}

接入时,只需要修改一处的代码。RedisTemplate redisTemplate = new MyRedisTemplate();

@Bean
@Primary
public RedisTemplate redisTemplate(JedisConnectionFactory jedisConnFactory) {
    RedisTemplate redisTemplate = new MyRedisTemplate();
  	//旧 RedisTemplate redisTemplate = new RedisTemplate(); 
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new StringRedisSerializer());
    redisTemplate.setConnectionFactory(jedisConnFactory);
    return redisTemplate;
}

以上只是一个刚毕业三个月的初级菜鸟的探索,如果有更好的方法欢迎留言讨论。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/332073.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号