在RedisTemplate执行lua脚本如果做呢? 查看下面方法代码,这是引入lua内容脚本到RedisScript
的子类DefaultRedisScript
类中,这是redis提供支持的脚本支持类,更多内容可参考=>redis脚本参考
protected RedisScript<Long> getRedisLockScript() { String script = "local key = ARGV[1];local expiration = ARGV[2];local value = 1;"; script += "if redis.call('EXISTS', key) == 1 then return -1 else redis.call('SET', key, value);redis.call('EXPIRE', key, expiration);return 1;end"; return new DefaultRedisScript<>(script, Long.class); }
这里可以通过查看execute方法查看执行的源码流程:
* 获取锁 eparam kex 大 @param expireSeconds 锁的过期时间 @return 获取到锁返回true,否则false public boolean tryLock(String key, int expireSeconds){ return stringRedisTemplate.execute(redisLockScript, EMPTY_LIST,key,String.value0f(expireSeconds)).intValue()==1;}
首先进入到RedisTemplate.java
中的execute()
方法中
LockServicejava x RedisTemplatejava x DefaultScriptExecutorjava x public <T>Texecute(RedisScript<T> script, List<k> keys, object... args) { return scriptExecutor.execute(script, keys, args); public <i>T execute(RedisScript<>> script,RedisSerializer<?> argsSerializer, RedisSerializer<>> resultserializer List<k> keys, object... args){ return scriptExecutor.execute(script,argsSerializer, resultSerializer, keys, args);
通过方法重载,找到具体的执行类方法,主要有以下4步:
execute()
三个参数的方法execute()
五个参数的方法execute()
五个参数的方法体- 最后
eval()
方法执行redis脚本命令
public <i>T execute(final RedisScript<T> script, final List<> keys, final Object... args) { // use the Template's value serializer for args and result return execute(script,template.getValueSerializer(),(RedisSerializer<T>) template.getValueSerializer(),keys, args); } public <T>Texecute(final RedisScript<T> script, final RedisSerializer<?> argsSerializer, final RedisSerializer<T> resultserializer, final List<k> keys, final Object... args) { return template.execute((RedisCallback)(connection)>{ final ReturnType returnType =ReturnType.fromJavaType(script.getResultType()): final byte[][] keysAndArgs = keysAndArgs(aresSerializer, keys, ares); final int keysize = keys != null ? keys.size() : 0; if(connection.isPipelined()l| connection.isQueueing()){ // We could script load first and then do evalsha to ensure sha is present,// but this adds a sha1 to exec/closePipeline results. Instead, just eval connection.eval(scriptBytes(script),returnType, keySize, keysAndArgs); return null;} return eval(connection, script, returnType, keySize, keysAndArgs, resultserializer); });
- 接下来进入到
eval()
方法的对应实现类DefaultScriptExecute
中: 首先直接传sha值,如果在Redis中找不到预加载的lua脚本导致报错,则catch住该错误,把整个脚本序列化后传入Redis进行执行:
protected<i>Teval(RedisConnection connection,RedisScript<T>script, ReturnType returnType, int numKeys, byte[][] keysAndArgs, RedisSerializer<T> resultSerializer){ Object result; try { result=connection.evalsha(script.getSha1(),returnType,numKeys,keysAndArgs); } catch(Exception e){ if(!exceptionContainsNoScriptError(e)){ throweinstanceof RuntimeException?(RuntimeException)e:new RedisSystemException(e.getMessage(), e); result = connection.eval(scriptBytes(script),returnType, numKeys,keysAndArgs); } if (script.getResultType() == null){ return null; } return deserializeResult(resultSerializer, result); }
我们查看以下脚本序列化的执行代码scriptBytes()
,其对应的执行类为StringRedisSerializer
类
keysAndArgsL1++」= argsserializer.serialize(arg); return keysAndArgs; protected byte[] scriptBytes(RedisScript<?> script) { return template.getStringSerializer().serialize(script.getScriptAsString()); Choose Implementation of FRedisSerializer.serialize(T) (7 found) GenericJackson2JsonRedisSerializer(org.springframework.data.redis.serializer) Maven:org.springframework.data:spring-data-redi GenericToStringSerializer(org.springframework.data.redis.serializer) Maven:org.springframework.data:spring-data-redi Jackson2JsonRedisSerializer(org.springframework.data.redis.serializer) Maven:org.springframework.data:spring-data-redi JacksonJsonRedisSerializer(org.springframework.data.redis.serializer) Maven:org.springframework.data:spring-data-redi JdkSerializationRedisSerializer(org.springframework.data.redis.serializer) Maven:org.springframework.data:spring-data-redi Oxmserializer(org.sprineframework.data.redis.serializer) Maven:org.springframework.data:spring-data-redi StringRedisSerializer(org.springframework.data.redis.serializer) Maven:org.springframew
进入到StringRedisSerializer
类,可以看出该序列化操作其实就是字符串String序列化操作
package org.springframework.data.redis.serializer; import /** Simple String to byte[](and back)serializer. Converts Strings into bytes and vice-versa using the specified charset ...*/ public class StringRedisSerializer implements RedisSerializer<String> { private final charset charset: public StringRedisSerializer(){this(Charset.forName("UTF8"));} @ public StringRedisSerializer(charset charset){ Assert.notNull(charset, message:"Charset must not be null!"): this.charset = charset; public string deserialize(byte[] bytes){ return(bytes == null ? null : new String(bytes, charset)); } public byte[] serialize(string string){ return(string == null ? null :string.getBytes(charset));
至此,关于SpringBoot中RedisTemplate的RedisScript源码分析结束~