RedisTemplate redis=(RedisTemplate)SpringUtil.getBean("redisTemplate");
redis.opsForValue().set("key1","value1",20, TimeUnit.HOURS);
if(redis.hasKey("key1")){
Long left=redis.getExpire("key1",TimeUnit.MILLISECONDS);
System.out.println(left);
Object result= redis.opsForValue().get("key1");
System.out.println(result);
}
1.解析 RedisTemplate redis=(RedisTemplate)SpringUtil.getBean("redisTemplate");
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
return this.getBeanFactory().getBean(name);
}
保证BeanFactory是Active,getBean(name)走到以下代码:
String beanName = this.transformedBeanName(name);
Object sharedInstance = this.getSingleton(beanName);
transformedBeanName是对字符串类型的name格式写法的验证
getSingleton走到:
Object singletonObject = this.singletonObjects.get(beanName);
singletonObjects的定义:
Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
2.解析: redis.opsForValue().set("key1","value1",20, TimeUnit.HOURS);
2.1opsForValue()解析
public ValueOperations<K, V> opsForValue() {
if (this.valueOps == null) {
this.valueOps = new DefaultValueOperations(this);
}
return this.valueOps;
}
this指RedisTemplate, DefaultValueOperations是一个类,它继承了AbstractOperations并实现了ValueOperations
valueOps 的类型ValueOperations<K, V> ,它是个接口
我感觉redis.opsForValue中redis是一个RedisTemplate,它的这个方法是把自己赋值给某个类(efaultValueOperations)的成员变量,并将这个类赋给了RedisTemplate中的一个接口成员。
以上这段迷之代码和链接中一样,将自己注册到一个实现了某个接口的类,并将这个类又赋值给了这个接口,并将这个接口作为自己的一个成员变量,意思可能是以后自己就可以用这个成员变量调这个接口的方法。接口可以调用实现类的引用,一个接口类型的引用变量来引用实现接口的类的实例。
2.2 set()解析
现将键值序列化成byte数组
public void set(K key, V value, final long timeout, final TimeUnit unit) {
final byte[] rawKey = this.rawKey(key);
final byte[] rawValue = this.rawValue(value);
this.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
this.potentiallyUsePsetEx(connection);
return null;
}
public void potentiallyUsePsetEx(RedisConnection connection) {
if (!TimeUnit.MILLISECONDS.equals(unit) || !this.failsafeInvokePsetEx(connection)) {
connection.setEx(rawKey, TimeoutUtils.toSeconds(timeout, unit), rawValue);
}
}
private boolean failsafeInvokePsetEx(RedisConnection connection) {
boolean failed = false;
try {
connection.pSetEx(rawKey, timeout, rawValue);
} catch (UnsupportedOperationException var4) {
failed = true;
}
return !failed;
}
}, true);
}
一点点解析:
RedisCallback是个接口,这个接口作为.execute的第一个参数,接口里是匿名内部类。
首先解释匿名内部类:
匿名内部类不能定义任何静态成员、方法。
匿名内部类中的方法不能是抽象的;
匿名内部类必须实现接口或抽象父类的所有抽象方法。
匿名内部类访问的外部类成员变量或成员方法必须用static修饰;
匿名内部类因为没有类名,可知匿名内部类不能定义构造器。
匿名内部类会继承一个父类(有且只有一个)或实现一个接口(有且只有一个),实现父类或接口中所有抽象方法,可以改写父类中的方法,添加自定义方法。
当匿名内部类和外部类有同名变量(方法)时,默认访问的是匿名内部类的变量(方法),要访问外部类的变量(方法)则需要加上外部类的类名。
实例演示,更多实例见以下链接
不使用匿名内部类来实现抽象方法
abstract class Person {
public abstract void eat();
}
class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}
运行结果:eat something
匿名内部类的基本实现
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
一种常见应用: Runnable接口的匿名内部类实现
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
Thread t = new Thread(r);
t.start();
}
}
ok,回归正题
this.execute()一步步封装了 execute,最最内层主要是获取RedisConnection一个连接拿到它的一个实例去执行 this.execute()里面的接口中的函数potentiallyUsePsetEx()
connection.setEx(rawKey, TimeoutUtils.toSeconds(timeout, unit), rawValue)
Converters.stringToBoolean(this.connection.getJedis().setex(key, (int)seconds, value));
public String setex(byte[] key, int seconds, byte[] value) {
this.checkIsInMultiOrPipeline(); //如果Jedis when in Multi 或 Pipeline 就抛异常
this.client.setex(key, seconds, value);
return this.client.getStatusCodeReply();
}
其中 this.client.setex(key, seconds, value);进入后:
this.sendCommand(Command.SETEX, new byte[][]{key, Protocol.toByteArray(seconds), value});
参数1为命令类型,参数2为字节二维数组,共三个一维数组,存了键值和过期时间
this.connect(); ///根据配置信息连接数据库
Protocol.sendCommand(this.outputStream, cmd, args); ///this.outputStream 为 RedisOutputStream
++this.pipelinedCommands;
return this; ///this是Connection
sendCommand()是将命令类型及各个参数写进输出流中,
总结:
在springboot中操作redis一般都是把参数序列化,命令写成标准形式然后一系列验证保符合基本规范,然后发总给redis服务。