DATAREDIS-1034 - Fix visibility issues in RedisTemplate.

Previously it was possible in multi-threaded environment to:
* get a `null` from `opsFor*` methods in RedisTemplate.
* fail with a `NullPointerException` when working with a not-`null` reference returned from `RedisTemplate.opsFor*` methods.

Introduced changes initialize the fields eagerly to avoid visibility issues when accessing them and add a `final` modifier to the `AbstractOperations#template` field.

Original pull request: #479.
This commit is contained in:
Denis Zavedeev
2019-09-12 20:43:55 +03:00
committed by Mark Paluch
parent 75806cc1e8
commit 64acdd2677
2 changed files with 12 additions and 38 deletions

View File

@@ -44,6 +44,7 @@ import org.springframework.util.CollectionUtils;
* @author Christoph Strobl
* @author David Liu
* @author Mark Paluch
* @author Denis Zavedeev
*/
abstract class AbstractOperations<K, V> {
@@ -64,7 +65,7 @@ abstract class AbstractOperations<K, V> {
protected abstract byte[] inRedis(byte[] rawKey, RedisConnection connection);
}
RedisTemplate<K, V> template;
final RedisTemplate<K, V> template;
AbstractOperations(RedisTemplate<K, V> template) {
this.template = template;

View File

@@ -81,6 +81,7 @@ import org.springframework.util.CollectionUtils;
* @author Ninad Divadkar
* @author Anqing Shao
* @author Mark Paluch
* @author Denis Zavedeev
* @param <K> the Redis key type against which the template works (usually a String)
* @param <V> the Redis value type against which the template works
* @see StringRedisTemplate
@@ -102,14 +103,14 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
private @Nullable ScriptExecutor<K> scriptExecutor;
// cache singleton objects (where possible)
private @Nullable ValueOperations<K, V> valueOps;
private @Nullable ListOperations<K, V> listOps;
private @Nullable SetOperations<K, V> setOps;
private @Nullable StreamOperations<K, ?, ?> streamOps;
private @Nullable ZSetOperations<K, V> zSetOps;
private @Nullable GeoOperations<K, V> geoOps;
private @Nullable HyperLogLogOperations<K, V> hllOps;
private final ValueOperations<K, V> valueOps = new DefaultValueOperations<>(this);
private final ListOperations<K, V> listOps = new DefaultListOperations<>(this);
private final SetOperations<K, V> setOps = new DefaultSetOperations<>(this);
private final StreamOperations<K, ?, ?> streamOps = new DefaultStreamOperations<>(this, new ObjectHashMapper());
private final ZSetOperations<K, V> zSetOps = new DefaultZSetOperations<>(this);
private final GeoOperations<K, V> geoOps = new DefaultGeoOperations<>(this);
private final HyperLogLogOperations<K, V> hllOps = new DefaultHyperLogLogOperations<>(this);
private final ClusterOperations<K, V> clusterOps = new DefaultClusterOperations<>(this);
/**
* Constructs a new <code>RedisTemplate</code> instance.
@@ -1203,7 +1204,7 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
*/
@Override
public ClusterOperations<K, V> opsForCluster() {
return new DefaultClusterOperations<>(this);
return clusterOps;
}
/*
@@ -1212,10 +1213,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
*/
@Override
public GeoOperations<K, V> opsForGeo() {
if (geoOps == null) {
geoOps = new DefaultGeoOperations<>(this);
}
return geoOps;
}
@@ -1252,10 +1249,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
*/
@Override
public HyperLogLogOperations<K, V> opsForHyperLogLog() {
if (hllOps == null) {
hllOps = new DefaultHyperLogLogOperations<>(this);
}
return hllOps;
}
@@ -1265,10 +1258,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
*/
@Override
public ListOperations<K, V> opsForList() {
if (listOps == null) {
listOps = new DefaultListOperations<>(this);
}
return listOps;
}
@@ -1296,10 +1285,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
*/
@Override
public SetOperations<K, V> opsForSet() {
if (setOps == null) {
setOps = new DefaultSetOperations<>(this);
}
return setOps;
}
@@ -1309,10 +1294,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
*/
@Override
public <HK, HV> StreamOperations<K, HK, HV> opsForStream() {
if (streamOps == null) {
streamOps = new DefaultStreamOperations<>(this, new ObjectHashMapper());
}
return (StreamOperations<K, HK, HV>) streamOps;
}
@@ -1350,10 +1331,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
*/
@Override
public ValueOperations<K, V> opsForValue() {
if (valueOps == null) {
valueOps = new DefaultValueOperations<>(this);
}
return valueOps;
}
@@ -1372,10 +1349,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
*/
@Override
public ZSetOperations<K, V> opsForZSet() {
if (zSetOps == null) {
zSetOps = new DefaultZSetOperations<>(this);
}
return zSetOps;
}