项目没有采用maven,所以出现版本问题需要去自己手动下载jar包才可以。
采用哨兵模式的前提是redis已经成功搭建,哨兵模式的配置非常简单,网上也有很多,不详细讲述如何搭建redis。我简单列出来一下spring的配置,本篇主要讲述配置完哨兵模式产生的问题以及解决方式。
在spring配置文件中加入
一共三个bean的配置,缺一不可!
问题一:搭建完之后启动项目,报错无法注入sentinels,理由是类型不一致!查看源码发现,的确不一致。源码的定义是
private Set
public void setSentinels(Iterable
notNull(sentinels, "Cannot set sentinels to 'null'.");
this.sentinels.clear();
for (RedisNode sentinel : sentinels) {
addSentinel(sentinel);
}
}
可以看的出来,定义的类型是set,而set的类型是iterable,虽然set跟iterable存在父子继承关系,但是还是无法注入,但是既然无法注入,为什么还要这么写呢!这是我在我头脑里面的问题。所以我大胆猜测,哨兵模式跟spring的版本有关系。于是开始搜索解决方案:
第一:由于项目中采用的是spring3.2的。网上好多人继承RedisSentinelConfiguration,参照了他们的方式,我发现存在一定的问题。为什么这么说,因为我也不清楚网上这么写的人到底是否真正的写成功了,但是的确存在继承问题。
修改spring的配置文件,变成自己定义的
看啊,要想解决继承问题,那么把重写set方法即可。把iterable变成set就解决了,于是我继承RedisSentinelConfiguration,重写下面的方法
public class RedisSentinelConfigurationChild extends RedisSentinelConfiguration {
public void setSentinels(Set
notNull(sentinels, "Cannot set sentinels to 'null'.");
this.sentinels.clear();
for (RedisNode sentinel : sentinels) {
addSentinel(sentinel);
}
}
}
请看我标注红色的地方,因为sentinels在父类中是私有方法,是不可以在子类中继承的。而大部分网上写继承的人,此处都没有改变,所以我才觉得他们应该是猜测这样,而没有真正的使用!当然,我们也可以通过父类的get方法来获取sentinels。比如这样
this.getSentinels().clear(); 写完之后也照样报错,报错是Collections导致的。
我也尝试了把父类所有的方法都重写一遍,但是我发现在其他spring的类里面创建的对象全部是用RedisSentinelConfiguration,如果我要改写,需要把所有用RedisSentinelConfiguration来创建对象的对方全部换成它的子类才可以,工作量大不说,解决起来也好难,所以,给后来人一个建议,继承RedisSentinelConfiguration不可用。此种方法抛弃。
第二个:升级spring,那么需要升级到多少版本呢,有的说4.0版本以上就可以。但是事实真的如此吗,并不是。需要spring4.0.7版本以上。
此处要感谢https://aperise.iteye.com/blog/2342615,提供对jedis以及spring的版本对应关系。
把spring4.0.7的版本替换原来的spring3.2版本,进行升级,升级完毕之后重启。
报错
Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy581
无法继承cglib的子类。啥意思,就是无法代理。
简单说一下代理,代理分为两种。一种为jdk的接口代理,接口代理需要目标类实现接口才可以。一种为cglib的代理,也叫作类代理,是通过继承的方式来实现的,继承的方式那就有局限性,因为并不是所有的东西都可以继承的,比如上文说的private或者final之类的。cglib的代理需要导入cglib-nodep.jar包。
那么代理用到哪里了呢。aop啊,事物啊,都会用到。
jdk的接口代理是程序默认的,如果想要强制使用cglib代理,那么需要加入proxy-target-class="true"。
所以,当出现以上报错的时候,要查找程序要哪些地方采用的代理。经过查找,发现项目中存在两处aop代理的地方。一种是基于接口代理,类似于这种。
还有一种
以上为两种aop的方式,定义拦截以及切入点。
经过查找,第二种方式这么写代理程序报错。开始解决
加入了
实现了接口,不好用。没有办法,只能重新写aop的拦截了,算是解决了问题。
但是一直有一个问题,为什么才spring的低版本下,没问题,而升级了高版本出现了问题了,所以也是猜测,只是猜测。
高版本的spring在程序没有指定代理方式的时候,会自己查找,如果目标类实现了接口,则用接口代理,如果没有接口,则使用类代理。但是目标类里面含有private或者final,无法继承导致报错,而低版本没有这个校验,甚至说低版本根本不查找代理方式,默认就是jdk的接口代理,如果指定了类代理,就用类代理。
当然,查找问题的过程中也走了很多的弯路,实在是网上查不到对应的解决方式。有些地方只能自己去看源码。