接入需要对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 MyRedisTemplateextends 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 RedisTemplateextends 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 ValueOperationsopsForValue() { if (valueOps == null) { valueOps = new DefaultValueOperations (this); } return valueOps; }
对于接口,怎样才能对DefaultValueOperations进行一个不同方法的不同逻辑的改造呢?DefaultValueOperations是ValueOperations的实现类,这个场景刚好适合静态代理模式:所谓静态代理也就是在程序运行前就已经存在代理类的字节码文件,实现方法如下面的类图,
如果一个目标类实现自接口,那么他的代理类同样实现接口,引入目标类的实例对象作为属性,通过目标类实现所有方法,但是真正调用的实例类是代理类。
DefaultValueOperationsProxy实现ValueOperrations,作为DefaultValueOperations的代理类,引入DefaultValueOperations作为私有属性,重写的方法使用DefaultValueOperations做原方法调用,也可以加入新逻辑。
public class DefaultValueOperationsProxyimplements 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 HotKeyRedisTemplateextends 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;
}
以上只是一个刚毕业三个月的初级菜鸟的探索,如果有更好的方法欢迎留言讨论。



