Replace Bound…Operations implementation with a proxy factory.

Original Pull Request: #2276
This commit is contained in:
Mark Paluch
2022-02-24 14:47:55 +01:00
committed by Christoph Strobl
parent 33e8974578
commit f5af2cf468
14 changed files with 407 additions and 1585 deletions

View File

@@ -0,0 +1,251 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
/**
* Utility to create implementation objects for {@code Bound…Operations} so that bound key interfaces can be implemented
* automatically by translating interface calls to actual {@code …Operations} interfaces.
*
* @author Mark Paluch
* @since 3.0
*/
class BoundOperationsProxyFactory {
private final Map<Method, Method> targetMethodCache = new ConcurrentHashMap<>();
/**
* Create a proxy object that implements {@link Class boundOperationsInterface} using the given {@code key} and
* {@link DataType}. Calls to {@code Bound…Operations} methods are bridged by forwarding these either to the
* {@code operationsTarget} or a default implementation.
*
* @param boundOperationsInterface the {@code Bound…Operations} interface.
* @param key the bound key.
* @param type the {@link DataType} for which to create a proxy object.
* @param operations the {@link RedisOperations} instance.
* @param operationsTargetFunction function to extract the actual delegate for method calls.
* @param <T>
* @return the proxy object.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T createProxy(Class<T> boundOperationsInterface, Object key, DataType type,
RedisOperations<?, ?> operations, Function<RedisOperations<?, ?>, Object> operationsTargetFunction) {
DefaultBoundKeyOperations delegate = new DefaultBoundKeyOperations(type, key, (RedisOperations) operations);
Object operationsTarget = operationsTargetFunction.apply(operations);
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addInterface(boundOperationsInterface);
proxyFactory.addAdvice(new DefaultMethodInvokingMethodInterceptor());
proxyFactory.addAdvice(
new BoundOperationsMethodInterceptor(key, operations, boundOperationsInterface, operationsTarget, delegate));
return (T) proxyFactory.getProxy();
}
Method lookupRequiredMethod(Method method, Class<?> targetClass, boolean considerKeyArgument) {
Method target = lookupMethod(method, targetClass, considerKeyArgument);
if (target == null) {
throw new IllegalArgumentException("Cannot lookup target method for %s in class %s. This appears to be a bug."
.formatted(method, targetClass.getName()));
}
return target;
}
@Nullable
Method lookupMethod(Method method, Class<?> targetClass, boolean considerKeyArgument) {
return targetMethodCache.computeIfAbsent(method, it -> {
Class[] paramTypes;
if (isStreamRead(method)) {
paramTypes = new Class[it.getParameterCount()];
System.arraycopy(it.getParameterTypes(), 0, paramTypes, 0, paramTypes.length - 1);
paramTypes[paramTypes.length - 1] = StreamOffset[].class;
} else if (considerKeyArgument) {
paramTypes = new Class[it.getParameterCount() + 1];
paramTypes[0] = Object.class;
System.arraycopy(it.getParameterTypes(), 0, paramTypes, 1, paramTypes.length - 1);
} else {
paramTypes = it.getParameterTypes();
}
return ReflectionUtils.findMethod(targetClass, method.getName(), paramTypes);
});
}
private boolean isStreamRead(Method method) {
return method.getName().equals("read")
&& method.getParameterTypes()[method.getParameterCount() - 1].equals(ReadOffset.class);
}
/**
* {@link MethodInterceptor} to delegate proxy calls to either {@link RedisOperations}, {@code key},
* {@link DefaultBoundKeyOperations} or the {@code operationsTarget} such as {@link ValueOperations}.
*/
class BoundOperationsMethodInterceptor implements MethodInterceptor {
private final Class<?> boundOperationsInterface;
private final Object operationsTarget;
private final DefaultBoundKeyOperations delegate;
public BoundOperationsMethodInterceptor(Object key, RedisOperations<?, ?> operations,
Class<?> boundOperationsInterface, Object operationsTarget, DefaultBoundKeyOperations delegate) {
this.boundOperationsInterface = boundOperationsInterface;
this.operationsTarget = operationsTarget;
this.delegate = delegate;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
switch (method.getName()) {
case "getKey":
return delegate.getKey();
case "rename":
delegate.rename(invocation.getArguments()[0]);
return null;
case "getOperations":
return delegate.getOps();
}
if (method.getDeclaringClass() == boundOperationsInterface) {
return doInvoke(invocation, method, operationsTarget, true);
}
return doInvoke(invocation, method, delegate, false);
}
@Nullable
private Object doInvoke(MethodInvocation invocation, Method method, Object target, boolean considerKeyArgument) {
Method backingMethod = lookupRequiredMethod(method, target.getClass(), considerKeyArgument);
Object[] args;
Object[] invocationArguments = invocation.getArguments();
if (isStreamRead(method)) {
// stream.read requires translation to StreamOffset using the bound key.
args = new Object[backingMethod.getParameterCount()];
System.arraycopy(invocationArguments, 0, args, 0, args.length - 1);
args[args.length - 1] = new StreamOffset[] {
StreamOffset.create(delegate.getKey(), (ReadOffset) invocationArguments[invocationArguments.length - 1]) };
} else if (backingMethod.getParameterCount() > 0 && backingMethod.getParameterTypes()[0].equals(Object.class)) {
args = new Object[backingMethod.getParameterCount()];
args[0] = delegate.getKey();
System.arraycopy(invocationArguments, 0, args, 1, args.length - 1);
} else {
args = invocationArguments;
}
try {
return backingMethod.invoke(target, args);
} catch (ReflectiveOperationException e) {
ReflectionUtils.handleReflectionException(e);
throw new UnsupportedOperationException("Should not happen", e);
}
}
}
/**
* Default {@link BoundKeyOperations} implementation. Meant for internal usage.
*
* @author Costin Leau
* @author Christoph Strobl
*/
static class DefaultBoundKeyOperations implements BoundKeyOperations<Object> {
private final DataType type;
private Object key;
private final RedisOperations<Object, ?> ops;
DefaultBoundKeyOperations(DataType type, Object key, RedisOperations<Object, ?> operations) {
this.type = type;
this.key = key;
this.ops = operations;
}
@Override
public Object getKey() {
return key;
}
@Override
public Boolean expire(long timeout, TimeUnit unit) {
return ops.expire(key, timeout, unit);
}
@Override
public Boolean expireAt(Date date) {
return ops.expireAt(key, date);
}
@Override
public Long getExpire() {
return ops.getExpire(key);
}
@Override
public Boolean persist() {
return ops.persist(key);
}
@Override
public void rename(Object newKey) {
if (ops.hasKey(key)) {
ops.rename(key, newKey);
}
key = newKey;
}
public DataType getType() {
return type;
}
public RedisOperations<Object, ?> getOps() {
return ops;
}
}
}

View File

@@ -182,9 +182,23 @@ public interface BoundSetOperations<K, V> extends BoundKeyOperations<K> {
* @param key must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/sdiff">Redis Documentation: SDIFF</a>
* @deprecated since 3.0, use {@link #difference(Object)} instead to follow a consistent method naming scheme.
*/
@Nullable
Set<V> diff(K key);
default Set<V> diff(K key) {
return difference(key);
}
/**
* Diff all sets for given the bound key and {@code key}.
*
* @param key must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 3.0
* @see <a href="https://redis.io/commands/sdiff">Redis Documentation: SDIFF</a>
*/
@Nullable
Set<V> difference(K key);
/**
* Diff all sets for given the bound key and {@code keys}.
@@ -192,9 +206,23 @@ public interface BoundSetOperations<K, V> extends BoundKeyOperations<K> {
* @param keys must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/sdiff">Redis Documentation: SDIFF</a>
* @deprecated since 3.0, use {@link #difference(Collection)} instead to follow a consistent method naming scheme.
*/
@Nullable
Set<V> diff(Collection<K> keys);
default Set<V> diff(Collection<K> keys) {
return difference(keys);
}
/**
* Diff all sets for given the bound key and {@code keys}.
*
* @param keys must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 3.0
* @see <a href="https://redis.io/commands/sdiff">Redis Documentation: SDIFF</a>
*/
@Nullable
Set<V> difference(Collection<K> keys);
/**
* Diff all sets for given the bound key and {@code keys} and store result in {@code destKey}.
@@ -202,8 +230,23 @@ public interface BoundSetOperations<K, V> extends BoundKeyOperations<K> {
* @param keys must not be {@literal null}.
* @param destKey must not be {@literal null}.
* @see <a href="https://redis.io/commands/sdiffstore">Redis Documentation: SDIFFSTORE</a>
* @deprecated since 3.0, use {@link #differenceAndStore(Object, Object)} instead to follow a consistent method naming
* scheme..
*/
void diffAndStore(K keys, K destKey);
@Deprecated
default void diffAndStore(K keys, K destKey) {
differenceAndStore(keys, destKey);
}
/**
* Diff all sets for given the bound key and {@code keys} and store result in {@code destKey}.
*
* @param keys must not be {@literal null}.
* @param destKey must not be {@literal null}.
* @since 3.0
* @see <a href="https://redis.io/commands/sdiffstore">Redis Documentation: SDIFFSTORE</a>
*/
void differenceAndStore(K keys, K destKey);
/**
* Diff all sets for given the bound key and {@code keys} and store result in {@code destKey}.
@@ -211,8 +254,23 @@ public interface BoundSetOperations<K, V> extends BoundKeyOperations<K> {
* @param keys must not be {@literal null}.
* @param destKey must not be {@literal null}.
* @see <a href="https://redis.io/commands/sdiffstore">Redis Documentation: SDIFFSTORE</a>
* @deprecated since 3.0, use {@link #differenceAndStore(Collection, Object)} instead to follow a consistent method
* naming scheme.
*/
void diffAndStore(Collection<K> keys, K destKey);
@Deprecated
default void diffAndStore(Collection<K> keys, K destKey) {
differenceAndStore(keys, destKey);
}
/**
* Diff all sets for given the bound key and {@code keys} and store result in {@code destKey}.
*
* @param keys must not be {@literal null}.
* @param destKey must not be {@literal null}.
* @since 3.0
* @see <a href="https://redis.io/commands/sdiffstore">Redis Documentation: SDIFFSTORE</a>
*/
void differenceAndStore(Collection<K> keys, K destKey);
/**
* Get all elements of set at the bound key.

View File

@@ -1,144 +0,0 @@
/*
* Copyright 2016-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.util.List;
import java.util.Map;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.domain.geo.GeoReference;
import org.springframework.data.redis.domain.geo.GeoShape;
/**
* Default implementation of {@link BoundGeoOperations}.
*
* @author Ninad Divadkar
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.8
*/
class DefaultBoundGeoOperations<K, M> extends DefaultBoundKeyOperations<K> implements BoundGeoOperations<K, M> {
private final GeoOperations<K, M> ops;
/**
* Constructs a new {@code DefaultBoundGeoOperations}.
*
* @param key must not be {@literal null}.
* @param operations must not be {@literal null}.
*/
DefaultBoundGeoOperations(K key, RedisOperations<K, M> operations) {
super(key, operations);
this.ops = operations.opsForGeo();
}
@Override
public Long add(Point point, M member) {
return ops.add(getKey(), point, member);
}
@Override
public Long add(GeoLocation<M> location) {
return ops.add(getKey(), location);
}
@Override
public Long add(Map<M, Point> memberCoordinateMap) {
return ops.add(getKey(), memberCoordinateMap);
}
@Override
public Long add(Iterable<GeoLocation<M>> locations) {
return ops.add(getKey(), locations);
}
@Override
public Distance distance(M member1, M member2) {
return ops.distance(getKey(), member1, member2);
}
@Override
public Distance distance(M member1, M member2, Metric unit) {
return ops.distance(getKey(), member1, member2, unit);
}
@Override
public List<String> hash(M... members) {
return ops.hash(getKey(), members);
}
@Override
public List<Point> position(M... members) {
return ops.position(getKey(), members);
}
@Override
public GeoResults<GeoLocation<M>> radius(Circle within) {
return ops.radius(getKey(), within);
}
@Override
public GeoResults<GeoLocation<M>> radius(Circle within, GeoRadiusCommandArgs param) {
return ops.radius(getKey(), within, param);
}
@Override
public GeoResults<GeoLocation<M>> radius(M member, double radius) {
return ops.radius(getKey(), member, radius);
}
@Override
public GeoResults<GeoLocation<M>> radius(M member, Distance distance) {
return ops.radius(getKey(), member, distance);
}
@Override
public GeoResults<GeoLocation<M>> radius(M member, Distance distance, GeoRadiusCommandArgs param) {
return ops.radius(getKey(), member, distance, param);
}
@Override
public Long remove(M... members) {
return ops.remove(getKey(), members);
}
@Override
public GeoResults<GeoLocation<M>> search(GeoReference<M> reference,
GeoShape geoPredicate, RedisGeoCommands.GeoSearchCommandArgs args) {
return ops.search(getKey(), reference, geoPredicate, args);
}
@Override
public Long searchAndStore(K destKey, GeoReference<M> reference,
GeoShape geoPredicate, RedisGeoCommands.GeoSearchStoreCommandArgs args) {
return ops.searchAndStore(getKey(), destKey, reference, geoPredicate, args);
}
@Override
public DataType getType() {
return DataType.ZSET;
}
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright 2011-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.data.redis.connection.DataType;
import org.springframework.lang.Nullable;
/**
* Default implementation for {@link HashOperations}.
*
* @author Costin Leau
* @author Christoph Strobl
* @author Ninad Divadkar
*/
class DefaultBoundHashOperations<H, HK, HV> extends DefaultBoundKeyOperations<H>
implements BoundHashOperations<H, HK, HV> {
private final HashOperations<H, HK, HV> ops;
/**
* Constructs a new <code>DefaultBoundHashOperations</code> instance.
*
* @param key
* @param operations
*/
DefaultBoundHashOperations(H key, RedisOperations<H, ?> operations) {
super(key, operations);
this.ops = operations.opsForHash();
}
@Override
public Long delete(Object... keys) {
return ops.delete(getKey(), keys);
}
@Override
public HV get(Object key) {
return ops.get(getKey(), key);
}
@Override
public List<HV> multiGet(Collection<HK> hashKeys) {
return ops.multiGet(getKey(), hashKeys);
}
@Override
public RedisOperations<H, ?> getOperations() {
return ops.getOperations();
}
@Override
public Boolean hasKey(Object key) {
return ops.hasKey(getKey(), key);
}
@Override
public Long increment(HK key, long delta) {
return ops.increment(getKey(), key, delta);
}
@Override
public Double increment(HK key, double delta) {
return ops.increment(getKey(), key, delta);
}
@Nullable
@Override
public HK randomKey() {
return ops.randomKey(getKey());
}
@Nullable
@Override
public Entry<HK, HV> randomEntry() {
return ops.randomEntry(getKey());
}
@Nullable
@Override
public List<HK> randomKeys(long count) {
return ops.randomKeys(getKey(), count);
}
@Nullable
@Override
public Map<HK, HV> randomEntries(long count) {
return ops.randomEntries(getKey(), count);
}
@Override
public Set<HK> keys() {
return ops.keys(getKey());
}
@Nullable
@Override
public Long lengthOfValue(HK hashKey) {
return ops.lengthOfValue(getKey(), hashKey);
}
@Override
public Long size() {
return ops.size(getKey());
}
@Override
public void putAll(Map<? extends HK, ? extends HV> m) {
ops.putAll(getKey(), m);
}
@Override
public void put(HK key, HV value) {
ops.put(getKey(), key, value);
}
@Override
public Boolean putIfAbsent(HK key, HV value) {
return ops.putIfAbsent(getKey(), key, value);
}
@Override
public List<HV> values() {
return ops.values(getKey());
}
@Override
public Map<HK, HV> entries() {
return ops.entries(getKey());
}
@Override
public DataType getType() {
return DataType.HASH;
}
@Override
public Cursor<Entry<HK, HV>> scan(ScanOptions options) {
return ops.scan(getKey(), options);
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2011-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Default {@link BoundKeyOperations} implementation. Meant for internal usage.
*
* @author Costin Leau
* @author Christoph Strobl
*/
abstract class DefaultBoundKeyOperations<K> implements BoundKeyOperations<K> {
private K key;
private final RedisOperations<K, ?> ops;
DefaultBoundKeyOperations(K key, RedisOperations<K, ?> operations) {
this.key = key;
this.ops = operations;
}
@Override
public K getKey() {
return key;
}
@Override
public Boolean expire(long timeout, TimeUnit unit) {
return ops.expire(key, timeout, unit);
}
@Override
public Boolean expireAt(Date date) {
return ops.expireAt(key, date);
}
@Override
public Long getExpire() {
return ops.getExpire(key);
}
@Override
public Boolean persist() {
return ops.persist(key);
}
@Override
public void rename(K newKey) {
if (ops.hasKey(key)) {
ops.rename(key, newKey);
}
key = newKey;
}
}

View File

@@ -1,181 +0,0 @@
/*
* Copyright 2011-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisListCommands.Direction;
/**
* Default implementation for {@link BoundListOperations}.
*
* @author Costin Leau
* @author Mark Paluch
*/
class DefaultBoundListOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundListOperations<K, V> {
private final ListOperations<K, V> ops;
/**
* Constructs a new <code>DefaultBoundListOperations</code> instance.
*
* @param key
* @param operations
*/
DefaultBoundListOperations(K key, RedisOperations<K, V> operations) {
super(key, operations);
this.ops = operations.opsForList();
}
@Override
public RedisOperations<K, V> getOperations() {
return ops.getOperations();
}
@Override
public V index(long index) {
return ops.index(getKey(), index);
}
@Override
public Long indexOf(V value) {
return ops.indexOf(getKey(), value);
}
@Override
public Long lastIndexOf(V value) {
return ops.lastIndexOf(getKey(), value);
}
@Override
public V leftPop() {
return ops.leftPop(getKey());
}
@Override
public List<V> leftPop(long count) {
return ops.leftPop(getKey(), count);
}
@Override
public V leftPop(long timeout, TimeUnit unit) {
return ops.leftPop(getKey(), timeout, unit);
}
@Override
public Long leftPush(V value) {
return ops.leftPush(getKey(), value);
}
@Override
public Long leftPushAll(V... values) {
return ops.leftPushAll(getKey(), values);
}
@Override
public Long leftPushIfPresent(V value) {
return ops.leftPushIfPresent(getKey(), value);
}
@Override
public Long leftPush(V pivot, V value) {
return ops.leftPush(getKey(), pivot, value);
}
@Override
public Long size() {
return ops.size(getKey());
}
@Override
public List<V> range(long start, long end) {
return ops.range(getKey(), start, end);
}
@Override
public Long remove(long i, Object value) {
return ops.remove(getKey(), i, value);
}
@Override
public V rightPop() {
return ops.rightPop(getKey());
}
@Override
public List<V> rightPop(long count) {
return ops.rightPop(getKey(), count);
}
@Override
public V rightPop(long timeout, TimeUnit unit) {
return ops.rightPop(getKey(), timeout, unit);
}
@Override
public Long rightPushIfPresent(V value) {
return ops.rightPushIfPresent(getKey(), value);
}
@Override
public Long rightPush(V value) {
return ops.rightPush(getKey(), value);
}
@Override
public Long rightPushAll(V... values) {
return ops.rightPushAll(getKey(), values);
}
@Override
public Long rightPush(V pivot, V value) {
return ops.rightPush(getKey(), pivot, value);
}
@Override
public V move(Direction from, K destinationKey, Direction to) {
return ops.move(getKey(), from, destinationKey, to);
}
@Override
public V move(Direction from, K destinationKey, Direction to, Duration timeout) {
return ops.move(getKey(), from, destinationKey, to, timeout);
}
@Override
public V move(Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit) {
return ops.move(getKey(), from, destinationKey, to, timeout, unit);
}
@Override
public void trim(long start, long end) {
ops.trim(getKey(), start, end);
}
@Override
public void set(long index, V value) {
ops.set(getKey(), index, value);
}
@Override
public DataType getType() {
return DataType.LIST;
}
}

View File

@@ -1,177 +0,0 @@
/*
* Copyright 2011-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.data.redis.connection.DataType;
/**
* Default implementation for {@link BoundSetOperations}.
*
* @author Costin Leau
* @author Christoph Strobl
*/
class DefaultBoundSetOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundSetOperations<K, V> {
private final SetOperations<K, V> ops;
/**
* Constructs a new <code>DefaultBoundSetOperations</code> instance.
*
* @param key
* @param operations
*/
DefaultBoundSetOperations(K key, RedisOperations<K, V> operations) {
super(key, operations);
this.ops = operations.opsForSet();
}
@Override
public Long add(V... values) {
return ops.add(getKey(), values);
}
@Override
public Set<V> diff(K key) {
return ops.difference(getKey(), key);
}
@Override
public Set<V> diff(Collection<K> keys) {
return ops.difference(getKey(), keys);
}
@Override
public void diffAndStore(K key, K destKey) {
ops.differenceAndStore(getKey(), key, destKey);
}
@Override
public void diffAndStore(Collection<K> keys, K destKey) {
ops.differenceAndStore(getKey(), keys, destKey);
}
@Override
public RedisOperations<K, V> getOperations() {
return ops.getOperations();
}
@Override
public Set<V> intersect(K key) {
return ops.intersect(getKey(), key);
}
@Override
public Set<V> intersect(Collection<K> keys) {
return ops.intersect(getKey(), keys);
}
@Override
public void intersectAndStore(K key, K destKey) {
ops.intersectAndStore(getKey(), key, destKey);
}
@Override
public void intersectAndStore(Collection<K> keys, K destKey) {
ops.intersectAndStore(getKey(), keys, destKey);
}
@Override
public Boolean isMember(Object o) {
return ops.isMember(getKey(), o);
}
@Override
public Map<Object, Boolean> isMember(Object... objects) {
return ops.isMember(getKey(), objects);
}
@Override
public Set<V> members() {
return ops.members(getKey());
}
@Override
public Boolean move(K destKey, V value) {
return ops.move(getKey(), value, destKey);
}
@Override
public V randomMember() {
return ops.randomMember(getKey());
}
@Override
public Set<V> distinctRandomMembers(long count) {
return ops.distinctRandomMembers(getKey(), count);
}
@Override
public List<V> randomMembers(long count) {
return ops.randomMembers(getKey(), count);
}
@Override
public Long remove(Object... values) {
return ops.remove(getKey(), values);
}
@Override
public V pop() {
return ops.pop(getKey());
}
@Override
public Long size() {
return ops.size(getKey());
}
@Override
public Set<V> union(K key) {
return ops.union(getKey(), key);
}
@Override
public Set<V> union(Collection<K> keys) {
return ops.union(getKey(), keys);
}
@Override
public void unionAndStore(K key, K destKey) {
ops.unionAndStore(getKey(), key, destKey);
}
@Override
public void unionAndStore(Collection<K> keys, K destKey) {
ops.unionAndStore(getKey(), keys, destKey);
}
@Override
public DataType getType() {
return DataType.SET;
}
@Override
public Cursor<V> scan(ScanOptions options) {
return ops.scan(getKey(), options);
}
}

View File

@@ -1,139 +0,0 @@
/*
* Copyright 2018-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.util.List;
import java.util.Map;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.Limit;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.lang.Nullable;
/**
* Default implementation for {@link BoundStreamOperations}.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.2
*/
class DefaultBoundStreamOperations<K, HK, HV> extends DefaultBoundKeyOperations<K>
implements BoundStreamOperations<K, HK, HV> {
private final StreamOperations<K, HK, HV> ops;
/**
* Constructs a new <code>DefaultBoundSetOperations</code> instance.
*
* @param key
* @param operations
*/
DefaultBoundStreamOperations(K key, RedisOperations<K, ?> operations) {
super(key, operations);
this.ops = operations.opsForStream();
}
@Nullable
@Override
public Long acknowledge(String group, String... recordIds) {
return ops.acknowledge(getKey(), group, recordIds);
}
@Nullable
@Override
public RecordId add(Map<HK, HV> body) {
return ops.add(getKey(), body);
}
@Nullable
@Override
public Long delete(String... recordIds) {
return ops.delete(getKey(), recordIds);
}
@Nullable
@Override
public String createGroup(ReadOffset readOffset, String group) {
return ops.createGroup(getKey(), readOffset, group);
}
@Nullable
@Override
public Boolean deleteConsumer(Consumer consumer) {
return ops.deleteConsumer(getKey(), consumer);
}
@Nullable
@Override
public Boolean destroyGroup(String group) {
return ops.destroyGroup(getKey(), group);
}
@Nullable
@Override
public Long size() {
return ops.size(getKey());
}
@Nullable
@Override
public List<MapRecord<K, HK, HV>> range(Range<String> range, Limit limit) {
return ops.range(getKey(), range, limit);
}
@Nullable
@Override
public List<MapRecord<K, HK, HV>> read(StreamReadOptions readOptions, ReadOffset readOffset) {
return ops.read(readOptions, StreamOffset.create(getKey(), readOffset));
}
@Nullable
@Override
public List<MapRecord<K, HK, HV>> read(Consumer consumer, StreamReadOptions readOptions, ReadOffset readOffset) {
return ops.read(consumer, readOptions, StreamOffset.create(getKey(), readOffset));
}
@Nullable
@Override
public List<MapRecord<K, HK, HV>> reverseRange(Range<String> range, Limit limit) {
return ops.reverseRange(getKey(), range, limit);
}
@Nullable
@Override
public Long trim(long count) {
return trim(count, false);
}
@Nullable
@Override
public Long trim(long count, boolean approximateTrimming) {
return ops.trim(getKey(), count, approximateTrimming);
}
@Nullable
@Override
public DataType getType() {
return DataType.STREAM;
}
}

View File

@@ -1,165 +0,0 @@
/*
* Copyright 2011-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.connection.DataType;
import org.springframework.lang.Nullable;
/**
* @author Costin Leau
* @author Jiahe Cai
* @author Mark Paluch
*/
class DefaultBoundValueOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundValueOperations<K, V> {
private final ValueOperations<K, V> ops;
/**
* Constructs a new {@link DefaultBoundValueOperations} instance.
*
* @param key
* @param operations
*/
DefaultBoundValueOperations(K key, RedisOperations<K, V> operations) {
super(key, operations);
this.ops = operations.opsForValue();
}
@Override
public V get() {
return ops.get(getKey());
}
@Nullable
@Override
public V getAndDelete() {
return ops.getAndDelete(getKey());
}
@Nullable
@Override
public V getAndExpire(long timeout, TimeUnit unit) {
return ops.getAndExpire(getKey(), timeout, unit);
}
@Nullable
@Override
public V getAndExpire(Duration timeout) {
return ops.getAndExpire(getKey(), timeout);
}
@Nullable
@Override
public V getAndPersist() {
return ops.getAndPersist(getKey());
}
@Override
public V getAndSet(V value) {
return ops.getAndSet(getKey(), value);
}
@Override
public Long increment() {
return ops.increment(getKey());
}
@Override
public Long increment(long delta) {
return ops.increment(getKey(), delta);
}
@Override
public Double increment(double delta) {
return ops.increment(getKey(), delta);
}
@Override
public Long decrement() {
return ops.decrement(getKey());
}
@Override
public Long decrement(long delta) {
return ops.decrement(getKey(), delta);
}
@Override
public Integer append(String value) {
return ops.append(getKey(), value);
}
@Override
public String get(long start, long end) {
return ops.get(getKey(), start, end);
}
@Override
public void set(V value, long timeout, TimeUnit unit) {
ops.set(getKey(), value, timeout, unit);
}
@Override
public void set(V value) {
ops.set(getKey(), value);
}
@Override
public Boolean setIfAbsent(V value) {
return ops.setIfAbsent(getKey(), value);
}
@Override
public Boolean setIfAbsent(V value, long timeout, TimeUnit unit) {
return ops.setIfAbsent(getKey(), value, timeout, unit);
}
@Nullable
@Override
public Boolean setIfPresent(V value) {
return ops.setIfPresent(getKey(), value);
}
@Nullable
@Override
public Boolean setIfPresent(V value, long timeout, TimeUnit unit) {
return ops.setIfPresent(getKey(), value, timeout, unit);
}
@Override
public void set(V value, long offset) {
ops.set(getKey(), value, offset);
}
@Override
public Long size() {
return ops.size(getKey());
}
@Override
public RedisOperations<K, V> getOperations() {
return ops.getOperations();
}
@Override
public DataType getType() {
return DataType.STRING;
}
}

View File

@@ -1,370 +0,0 @@
/*
* Copyright 2011-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.Limit;
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
import org.springframework.data.redis.connection.RedisZSetCommands.Range;
import org.springframework.data.redis.connection.zset.Weights;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.lang.Nullable;
/**
* Default implementation for {@link BoundZSetOperations}.
*
* @author Costin Leau
* @author Christoph Strobl
* @author Mark Paluch
* @author Wongoo (望哥)
* @author Andrey Shlykov
*/
class DefaultBoundZSetOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundZSetOperations<K, V> {
private final ZSetOperations<K, V> ops;
/**
* Constructs a new <code>DefaultBoundZSetOperations</code> instance.
*
* @param key
* @param operations
*/
DefaultBoundZSetOperations(K key, RedisOperations<K, V> operations) {
super(key, operations);
this.ops = operations.opsForZSet();
}
@Override
public Boolean add(V value, double score) {
return ops.add(getKey(), value, score);
}
@Override
public Boolean addIfAbsent(V value, double score) {
return ops.addIfAbsent(getKey(), value, score);
}
@Override
public Long add(Set<TypedTuple<V>> tuples) {
return ops.add(getKey(), tuples);
}
@Override
public Long addIfAbsent(Set<TypedTuple<V>> tuples) {
return ops.addIfAbsent(getKey(), tuples);
}
@Override
public Double incrementScore(V value, double delta) {
return ops.incrementScore(getKey(), value, delta);
}
@Override
public V randomMember() {
return ops.randomMember(getKey());
}
@Nullable
@Override
public Set<V> distinctRandomMembers(long count) {
return ops.distinctRandomMembers(getKey(), count);
}
@Nullable
@Override
public List<V> randomMembers(long count) {
return ops.randomMembers(getKey(), count);
}
@Override
public TypedTuple<V> randomMemberWithScore() {
return ops.randomMemberWithScore(getKey());
}
@Nullable
@Override
public Set<TypedTuple<V>> distinctRandomMembersWithScore(long count) {
return ops.distinctRandomMembersWithScore(getKey(), count);
}
@Nullable
@Override
public List<TypedTuple<V>> randomMembersWithScore(long count) {
return ops.randomMembersWithScore(getKey(), count);
}
@Override
public RedisOperations<K, V> getOperations() {
return ops.getOperations();
}
@Override
public Set<V> range(long start, long end) {
return ops.range(getKey(), start, end);
}
@Override
public Set<V> rangeByScore(double min, double max) {
return ops.rangeByScore(getKey(), min, max);
}
@Override
public Set<TypedTuple<V>> rangeByScoreWithScores(double min, double max) {
return ops.rangeByScoreWithScores(getKey(), min, max);
}
@Override
public Set<TypedTuple<V>> rangeWithScores(long start, long end) {
return ops.rangeWithScores(getKey(), start, end);
}
@Override
public Set<V> reverseRangeByScore(double min, double max) {
return ops.reverseRangeByScore(getKey(), min, max);
}
@Override
public Set<TypedTuple<V>> reverseRangeByScoreWithScores(double min, double max) {
return ops.reverseRangeByScoreWithScores(getKey(), min, max);
}
@Override
public Set<TypedTuple<V>> reverseRangeWithScores(long start, long end) {
return ops.reverseRangeWithScores(getKey(), start, end);
}
@Override
public Set<V> rangeByLex(Range range, Limit limit) {
return ops.rangeByLex(getKey(), range, limit);
}
@Override
public Set<V> reverseRangeByLex(Range range, Limit limit) {
return ops.reverseRangeByLex(getKey(), range, limit);
}
@Override
public Long rank(Object o) {
return ops.rank(getKey(), o);
}
@Override
public Long reverseRank(Object o) {
return ops.reverseRank(getKey(), o);
}
@Override
public Double score(Object o) {
return ops.score(getKey(), o);
}
@Override
public List<Double> score(Object... o) {
return ops.score(getKey(), o);
}
@Override
public Long remove(Object... values) {
return ops.remove(getKey(), values);
}
@Override
public Long removeRange(long start, long end) {
return ops.removeRange(getKey(), start, end);
}
@Override
public Long removeRangeByLex(Range range) {
return ops.removeRangeByLex(getKey(), range);
}
@Override
public Long removeRangeByScore(double min, double max) {
return ops.removeRangeByScore(getKey(), min, max);
}
@Override
public Set<V> reverseRange(long start, long end) {
return ops.reverseRange(getKey(), start, end);
}
@Override
public Long count(double min, double max) {
return ops.count(getKey(), min, max);
}
@Override
public Long lexCount(Range range) {
return ops.lexCount(getKey(), range);
}
@Nullable
@Override
public TypedTuple<V> popMin() {
return ops.popMin(getKey());
}
@Nullable
@Override
public Set<TypedTuple<V>> popMin(long count) {
return ops.popMin(getKey(), count);
}
@Nullable
@Override
public TypedTuple<V> popMin(long timeout, TimeUnit unit) {
return ops.popMin(getKey(), timeout, unit);
}
@Nullable
@Override
public TypedTuple<V> popMax() {
return ops.popMax(getKey());
}
@Nullable
@Override
public Set<TypedTuple<V>> popMax(long count) {
return ops.popMax(getKey(), count);
}
@Nullable
@Override
public TypedTuple<V> popMax(long timeout, TimeUnit unit) {
return ops.popMax(getKey(), timeout, unit);
}
@Override
public Long size() {
return zCard();
}
@Override
public Long zCard() {
return ops.zCard(getKey());
}
@Nullable
@Override
public Set<V> difference(Collection<K> otherKeys) {
return ops.difference(getKey(), otherKeys);
}
@Nullable
@Override
public Set<TypedTuple<V>> differenceWithScores(Collection<K> otherKeys) {
return ops.differenceWithScores(getKey(), otherKeys);
}
@Nullable
@Override
public Long differenceAndStore(Collection<K> otherKeys, K destKey) {
return ops.differenceAndStore(getKey(), otherKeys, destKey);
}
@Nullable
@Override
public Set<V> intersect(Collection<K> otherKeys) {
return ops.intersect(getKey(), otherKeys);
}
@Nullable
@Override
public Set<TypedTuple<V>> intersectWithScores(Collection<K> otherKeys) {
return ops.intersectWithScores(getKey(), otherKeys);
}
@Nullable
@Override
public Set<TypedTuple<V>> intersectWithScores(Collection<K> otherKeys, Aggregate aggregate, Weights weights) {
return ops.intersectWithScores(getKey(), otherKeys, aggregate, weights);
}
@Override
public Long intersectAndStore(K otherKey, K destKey) {
return ops.intersectAndStore(getKey(), otherKey, destKey);
}
@Override
public Long intersectAndStore(Collection<K> otherKeys, K destKey) {
return ops.intersectAndStore(getKey(), otherKeys, destKey);
}
@Override
public Long intersectAndStore(Collection<K> otherKeys, K destKey, Aggregate aggregate) {
return ops.intersectAndStore(getKey(), otherKeys, destKey, aggregate);
}
@Override
public Long intersectAndStore(Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights) {
return ops.intersectAndStore(getKey(), otherKeys, destKey, aggregate, weights);
}
@Nullable
@Override
public Set<V> union(Collection<K> otherKeys) {
return ops.union(getKey(), otherKeys);
}
@Nullable
@Override
public Set<TypedTuple<V>> unionWithScores(Collection<K> otherKeys) {
return ops.unionWithScores(getKey(), otherKeys);
}
@Nullable
@Override
public Set<TypedTuple<V>> unionWithScores(Collection<K> otherKeys, Aggregate aggregate, Weights weights) {
return ops.unionWithScores(getKey(), otherKeys, aggregate, weights);
}
@Override
public Long unionAndStore(K otherKey, K destKey) {
return ops.unionAndStore(getKey(), otherKey, destKey);
}
@Override
public Long unionAndStore(Collection<K> otherKeys, K destKey) {
return ops.unionAndStore(getKey(), otherKeys, destKey);
}
@Override
public Long unionAndStore(Collection<K> otherKeys, K destKey, Aggregate aggregate) {
return ops.unionAndStore(getKey(), otherKeys, destKey, aggregate);
}
@Override
public Long unionAndStore(Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights) {
return ops.unionAndStore(getKey(), otherKeys, destKey, aggregate, weights);
}
@Override
public DataType getType() {
return DataType.ZSET;
}
@Override
public Cursor<TypedTuple<V>> scan(ScanOptions options) {
return ops.scan(getKey(), options);
}
}

View File

@@ -105,6 +105,7 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
private @Nullable ScriptExecutor<K> scriptExecutor;
private final BoundOperationsProxyFactory boundOperations = new BoundOperationsProxyFactory();
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);
@@ -1056,12 +1057,12 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
@Override
public BoundGeoOperations<K, V> boundGeoOps(K key) {
return new DefaultBoundGeoOperations<>(key, this);
return boundOperations.createProxy(BoundGeoOperations.class, key, DataType.ZSET, this, RedisOperations::opsForGeo);
}
@Override
public <HK, HV> BoundHashOperations<K, HK, HV> boundHashOps(K key) {
return new DefaultBoundHashOperations<>(key, this);
return boundOperations.createProxy(BoundHashOperations.class, key, DataType.HASH, this, it -> it.opsForHash());
}
@Override
@@ -1081,12 +1082,13 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
@Override
public BoundListOperations<K, V> boundListOps(K key) {
return new DefaultBoundListOperations<>(key, this);
return boundOperations.createProxy(BoundListOperations.class, key, DataType.LIST, this,
RedisOperations::opsForList);
}
@Override
public BoundSetOperations<K, V> boundSetOps(K key) {
return new DefaultBoundSetOperations<>(key, this);
return boundOperations.createProxy(BoundSetOperations.class, key, DataType.SET, this, RedisOperations::opsForSet);
}
@Override
@@ -1101,18 +1103,18 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
@Override
public <HK, HV> StreamOperations<K, HK, HV> opsForStream(HashMapper<? super K, ? super HK, ? super HV> hashMapper) {
return new DefaultStreamOperations<>(this, hashMapper);
}
@Override
public <HK, HV> BoundStreamOperations<K, HK, HV> boundStreamOps(K key) {
return new DefaultBoundStreamOperations<>(key, this);
return boundOperations.createProxy(BoundStreamOperations.class, key, DataType.STREAM, this, it -> opsForStream());
}
@Override
public BoundValueOperations<K, V> boundValueOps(K key) {
return new DefaultBoundValueOperations<>(key, this);
return boundOperations.createProxy(BoundValueOperations.class, key, DataType.STRING, this,
RedisOperations::opsForValue);
}
@Override
@@ -1122,7 +1124,8 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
@Override
public BoundZSetOperations<K, V> boundZSetOps(K key) {
return new DefaultBoundZSetOperations<>(key, this);
return boundOperations.createProxy(BoundZSetOperations.class, key, DataType.ZSET, this,
RedisOperations::opsForZSet);
}
@Override