在 RedisTemplate 中,定义了几个 execute() 方法,这些方法是 RedisTemplate 的核心方法。RedisTemplate 中很多其他方法均是通过调用 execute 来执行具体的操作。例如:
/* * (non-Javadoc) * @see org.springframework.data.redis.core.RedisOperations#delete(java.util.Collection) */ @Override public Long delete(Collection<K> keys) { if (CollectionUtils.isEmpty(keys)) { return 0L; } byte[][] rawKeys = rawKeys(keys); return execute(connection -> connection.del(rawKeys), true); }
上述方法是 RedisTemplate 中 delete 方法的源码,它就是使用 execute() 来执行具体的删除操作(即调用 connection.del(rawKeys) 方法)。
方法说明如下表:
方法定义 | 方法说明 |
<T> T execute(RedisCallback<T> action) | 在 Redis 连接中执行给定的操作 |
<T> T execute(RedisCallback<T> action, boolean exposeConnection) | 在连接中执行给定的操作对象,可以公开也可以不公开。 |
<T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) | 在可以公开或不公开的连接中执行给定的操作对象。 |
<T> T execute(RedisScript<T> script, List<K> keys, Object... args) | 执行给定的 RedisScript |
<T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer, List<K> keys, Object... args) | 执行给定的 RedisScript,使用提供的 RedisSerializers 序列化脚本参数和结果。 |
<T> T execute(SessionCallback<T> session) | 执行 Redis 会话 |
使用 RedisTemplate 直接调用 opsFor** 来操作 Redis 数据库,每执行一条命令是要重新拿一个连接,因此很耗资源。如果让一个连接直接执行多条语句的方法就是使用 RedisCallback(它太复杂,不常用),推荐使用 SessionCallback。
本例将演示使用 RedisCallback 向 Redis 写入数据,然后再将写入的数据取出来,输出到控制台。如下:
package com.hxstrive.redis.redistemplate.execute; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class ExecuteSimple { /** 注入 RedisTemplate */ @Autowired private RedisTemplate<String,String> redisTemplate; @Test public void contextLoads() { redisTemplate.execute(new RedisCallback<String>() { @Override public String doInRedis(RedisConnection connection) throws DataAccessException { RedisStringCommands commands = connection.stringCommands(); // 写入缓存 commands.set("execute_key".getBytes(), "hello world".getBytes()); // 从缓存获取值 byte[] value = commands.get("execute_key".getBytes()); System.out.println(new String(value)); return null; } }); } }
运行示例,输出结果如下:
hello world
其实,在 RedisTemplate 中,其他很多方法均是通过调用 execute() 方法来实现,只是不同的方法实现不同的回调接口。部分源码如下:
// ... @Override public Long increment(K key) { byte[] rawKey = rawKey(key); return execute(connection -> connection.incr(rawKey), true); } /* * (non-Javadoc) * @see org.springframework.data.redis.core.ValueOperations#increment(java.lang.Object, long) */ @Override public Long increment(K key, long delta) { byte[] rawKey = rawKey(key); return execute(connection -> connection.incrBy(rawKey, delta), true); } @Override public void set(K key, V value, long timeout, TimeUnit unit) { byte[] rawKey = rawKey(key); byte[] rawValue = rawValue(value); execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { potentiallyUsePsetEx(connection); return null; } public void potentiallyUsePsetEx(RedisConnection connection) { if (!TimeUnit.MILLISECONDS.equals(unit) || !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 e) { // in case the connection does not support pSetEx return false to allow fallback to other operation. failed = true; } return !failed; } }, true); } // ...
使用 RedisTemplate 直接调用 opsFor** 来操作 Redis 数据库,每执行一条命令是要重新拿一个连接,因此很耗资源。如果让一个连接直接执行多条语句的方法就是使用 SessionCallback,还可以使用 RedisCallback(它太复杂,不常用)。
package com.hxstrive.redis.redistemplate.execute; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SessionCallback; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class SessionCallbackSimple { /** 注入 RedisTemplate */ @Autowired private RedisTemplate<String,String> redisTemplate; @Test public void contextLoads() { redisTemplate.execute(new SessionCallback() { @Override public String execute(RedisOperations operations) throws DataAccessException { operations.opsForValue().set("valueK1", "value1"); System.out.println("valueK1 = " + operations.opsForValue().get("valueK1")); operations.opsForList().leftPushAll("listK1", "one", "two"); System.out.println("listK1 = " + operations.opsForList().size("listK1")); return null; } }); } }
运行示例,输出如下:
valueK1 = value1 listK1 = 2
该示例使用 Lua 脚本实现获取锁的功能,代码如下:
package com.hxstrive.redis.redistemplate.execute; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.test.context.junit4.SpringRunner; import java.util.Collections; @RunWith(SpringRunner.class) @SpringBootTest public class RedisScriptSimple { /** 注入 RedisTemplate */ @Autowired private RedisTemplate<String,String> redisTemplate; @Test public void contextLoads() { // 使用 lua 脚本实现获取锁 // ARGV[1] = lock-key // ARGV[2] = 30 String script = "local key = ARGV[1];" + "local expiration = ARGV[2];" + "local value = 1;" + "if redis.call('EXISTS', key) == 1 then " + " return -1 " + // 如果键存在,则返回-1 "else " + " redis.call('SET', key, value);" + // 如果键不存在,则设置键和值 " redis.call('EXPIRE', key, expiration);" + // 为键设置过期时间 " return 1;" + // 返回1,表示锁获取成功 "end"; DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class); // 其中,lock-key 表示锁的键,30 表示过期时间为30秒 Long val = redisTemplate.execute(redisScript, Collections.EMPTY_LIST, "lock-key", "30"); System.out.println("val = " + val); // 如果锁获取成功,则进入下一步操作 if(null != val && val == 1) { System.out.println("获取锁成功"); } else { System.err.println("获取锁失败"); } } }
运行示例,输出如下:
val = 1 获取锁成功