3232import org .springframework .transaction .support .TransactionSynchronization ;
3333import org .springframework .transaction .support .TransactionSynchronizationManager ;
3434import org .springframework .util .Assert ;
35+ import org .springframework .util .ReflectionUtils ;
36+ import org .springframework .util .StringUtils ;
3537
3638/**
3739 * Helper class that provides static methods for obtaining {@link RedisConnection} from a
@@ -320,8 +322,8 @@ private static RedisConnection getTargetConnection(RedisConnection connection) {
320322 */
321323 public static void unbindConnection (RedisConnectionFactory factory ) {
322324
323- RedisConnectionHolder connectionHolder =
324- ( RedisConnectionHolder ) TransactionSynchronizationManager .getResource (factory );
325+ RedisConnectionHolder connectionHolder = ( RedisConnectionHolder ) TransactionSynchronizationManager
326+ .getResource (factory );
325327
326328 if (connectionHolder == null ) {
327329 return ;
@@ -358,7 +360,8 @@ public static void unbindConnection(RedisConnectionFactory factory) {
358360 * @param connectionFactory Redis connection factory that the connection was created with
359361 * @return whether the connection is transactional or not
360362 */
361- public static boolean isConnectionTransactional (RedisConnection connection , RedisConnectionFactory connectionFactory ) {
363+ public static boolean isConnectionTransactional (RedisConnection connection ,
364+ RedisConnectionFactory connectionFactory ) {
362365
363366 Assert .notNull (connectionFactory , "No RedisConnectionFactory specified" );
364367
@@ -448,9 +451,16 @@ public void afterCompletion(int status) {
448451 static class ConnectionSplittingInterceptor implements MethodInterceptor {
449452
450453 private final RedisConnectionFactory factory ;
454+ private final @ Nullable String commandInterface ;
451455
452456 public ConnectionSplittingInterceptor (RedisConnectionFactory factory ) {
453457 this .factory = factory ;
458+ this .commandInterface = null ;
459+ }
460+
461+ public ConnectionSplittingInterceptor (RedisConnectionFactory factory , String commandInterface ) {
462+ this .factory = factory ;
463+ this .commandInterface = commandInterface ;
454464 }
455465
456466 @ Override
@@ -465,6 +475,22 @@ public Object intercept(Object obj, Method method, Object[] args) throws Throwab
465475 return obj ;
466476 }
467477
478+ Class <?> returnType = method .getReturnType ();
479+ String returnTypeName = returnType .getSimpleName ();
480+
481+ // bridge keyCommands etc. to defer target invocations
482+ if (returnType .isInterface () && returnType .getPackageName ().equals ("org.springframework.data.redis.connection" )
483+ && returnTypeName .startsWith ("Redis" ) && returnTypeName .endsWith ("Commands" )) {
484+
485+ ProxyFactory proxyFactory = new ProxyFactory (ReflectionUtils .invokeMethod (method , obj ));
486+
487+ proxyFactory .addAdvice (new ConnectionSplittingInterceptor (factory , method .getName ()));
488+ proxyFactory .addInterface (RedisConnectionProxy .class );
489+ proxyFactory .addInterface (returnType );
490+
491+ return proxyFactory .getProxy ();
492+ }
493+
468494 RedisCommand commandToExecute = RedisCommand .failsafeCommandLookup (method .getName ());
469495
470496 if (isPotentiallyThreadBoundCommand (commandToExecute )) {
@@ -481,9 +507,15 @@ public Object intercept(Object obj, Method method, Object[] args) throws Throwab
481507 }
482508
483509 RedisConnection connection = factory .getConnection ();
484-
510+ Object target = connection ;
485511 try {
486- return invoke (method , connection , args );
512+
513+ if (StringUtils .hasText (commandInterface )) {
514+ target = ReflectionUtils .invokeMethod (ReflectionUtils .findMethod (RedisConnection .class , commandInterface ),
515+ connection );
516+ }
517+
518+ return invoke (method , target , args );
487519 } finally {
488520 // properly close the unbound connection after executing command
489521 if (!connection .isClosed ()) {
0 commit comments