22 springboot中对redis操作的源码解析

 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服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值