Spring中模板模式和回调模式(二)

我们首先来看下面一段代码,这段代码是我们使用Jedis封装服务的一个实现:

[java]  view plain  copy
  1. @Service  
  2. public class JedisSpringDemo {  
  3.     @Resource(name = "shardedJedisPool")  
  4.     private ShardedJedisPool shardedJedisPool;  
  5.       
  6.     public String set(String key, String value){  
  7.         ShardedJedis shardedJedis = null;  
  8.         try{  
  9.             // 从连接池中获取jedis分片对象  
  10.             shardedJedis = shardedJedisPool.getResource();  
  11.             // 设置值到redis中  
  12.             return shardedJedis.set(key, value);  
  13.         }catch (Exception e){  
  14.             System.out.println(e.getMessage());  
  15.         }finally {  
  16.             if(null != shardedJedis){  
  17.                 shardedJedis.close();  
  18.             }  
  19.         }  
  20.         return null;  
  21.     }  
  22. }  
从上面的代码中,不知道大家有没有看出什么问题出来?就我看来,上面的这段代码违反了 DRY 原则,怎么说了,上面代码中的 try catch finally 中的绝大部分代码都是雷同的,唯一不同的就是我们 return 的那一行具体的调用方法,如果像这种方法很多的话 (jedis 提供了几十种类似的方法 ) ,我们的代码重复率是很高的,代码重复率一旦高起来,相应的维护成本也会提高,下面我们就来引进一种改进方法 -- 回调机制。

首先,我们创建一个接口类,该接口定义Jedis的操作,代码如下:

[java]  view plain  copy
  1. public interface RedisOperations {  
  2.      T execute(ConnectionCallback action);  
  3.    String set(final String key, final String value);  
  4.    String get(final String key);  
  5. }  
其次,定义连接 Redis 服务器的回调接口,代码如下:
[java]  view plain  copy
  1. public interface ConnectionCallback {  
  2.     T doInRedis(ShardedJedis shardedJedis);  
  3. }  
最后定义具体的操作服务类,代码如下:
[java]  view plain  copy
  1. @Service("redisTemplate")  
  2. public class RedisTemplate implements RedisOperations{  
  3.     @Resource(name = "shardedJedisPool")  
  4.     private ShardedJedisPool shardedJedisPool;  
  5.       
  6.     @Override  
  7.     public  T execute(ConnectionCallback action) {  
  8.         ShardedJedis shardedJedis = null;  
  9.         try{  
  10.             // 从连接池中获取jedis分片对象  
  11.             shardedJedis = shardedJedisPool.getResource();  
  12.               
  13.             return action.doInRedis(shardedJedis);  
  14.               
  15.         }catch (Exception e){  
  16.             System.out.println(e.getMessage());  
  17.         }finally {  
  18.             if(null != shardedJedis){  
  19.                 shardedJedis.close();  
  20.             }  
  21.         }  
  22.         return null;  
  23.     }  
  24.       
  25.    /** 
  26.      * attention:真正封装的方法,非常的简洁干脆 
  27.      */  
  28.     public String set(final String key, final String value){  
  29.         return execute(new ConnectionCallback() {  
  30.             @Override  
  31.             public String doInRedis(  
  32.                     ShardedJedis shardedJedis) {  
  33.                 return shardedJedis.set(key, value);  
  34.             }  
  35.         });  
  36.     }  
  37.       
  38.     public String get(final String key){  
  39.         return execute(new ConnectionCallback(){  
  40.             @Override  
  41.             public String doInRedis(ShardedJedis shardedJedis) {  
  42.                 return shardedJedis.get(key);  
  43.             }  
  44.         });  
  45.     }  
  46. }  
通过上面的代码,我们可以清晰的看到,将 try catch finally 部分的公共代码都封装到了回调函数中,当调用具体方法的时候,再实现回调方法的具体业务逻辑即可,代码的复用率更高了。

如果大家对spring jdbc或者是spring data Redis的源码研究过,就应该知道JdbcTemplateRedisTemplate这两个类,这两个框架中用到了大量的callback机制,下面我们就以spring data redis为例,来简单的看下高手是如何玩转callback机制的。

首先定义回调方法,代码如下:

[java]  view plain  copy
  1. public interface RedisCallback {  
  2.     T doInRedis(RedisConnection connection) throws DataAccessException;  
  3. }  
其次,定义操作方法,代码如下:
[java]  view plain  copy
  1. public interface RedisOperations {  
  2.      T execute(RedisCallback action);  
  3.      T execute(SessionCallback session);  
  4.   
  5.    …………省略若干方法…………  
  6.   
  7. }  
最后,回调机制的实现 RedisTemplate 类,代码如下:
[java]  view plain  copy
  1. public class RedisTemplate extends RedisAccessor implements RedisOperations {  
  2.   
  3.     // 以下定义的是Redis支持的操作类型,例如SetOperations就是用来操作Set类型的,由于Redis支持的操作类型比较多,所以将每种操作类型都抽象成一个具体的操作类  
  4.     private ValueOperations valueOps;  
  5.     private ListOperations listOps;  
  6.     private SetOperations setOps;  
  7.     private ZSetOperations zSetOps;  
  8.     private HyperLogLogOperations hllOps;  
  9.   
  10.     /** 
  11.      * Constructs a new RedisTemplate instance. 
  12.      */  
  13.     public RedisTemplate() {}  
  14.   
  15.     public  T execute(RedisCallback action) {  
  16.         return execute(action, isExposeConnection());  
  17.     }  
  18.   
  19.     public  T execute(RedisCallback action, boolean exposeConnection) {  
  20.         return execute(action, exposeConnection, false);  
  21.     }  
  22.   
  23.     public  T execute(RedisCallback action, boolean exposeConnection, boolean pipeline) {  
  24.         Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");  
  25.         Assert.notNull(action, "Callback object must not be null");  
  26.        // 获取Redis服务器的连接工厂  
  27.         RedisConnectionFactory factory = getConnectionFactory();  
  28.         RedisConnection conn = null;  
  29.         try {  
  30.   
  31.             if (enableTransactionSupport) {  
  32.                 // only bind resources in case of potential transaction synchronization  
  33.              // 如果开启了事物的话,需将连接绑定到事物上  
  34.                 conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);  
  35.             } else {  
  36.             // 获取连接  
  37.                 conn = RedisConnectionUtils.getConnection(factory);  
  38.             }  
  39.   
  40.             boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);  
  41.   
  42.             RedisConnection connToUse = preProcessConnection(conn, existingConnection);  
  43.   
  44.             boolean pipelineStatus = connToUse.isPipelined();  
  45.             if (pipeline && !pipelineStatus) {  
  46.                 connToUse.openPipeline();  
  47.             }  
  48.   
  49.             RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));  
  50.             T result = action.doInRedis(connToExpose);  
  51.   
  52.             // close pipeline  
  53.             if (pipeline && !pipelineStatus) {  
  54.                 connToUse.closePipeline();  
  55.             }  
  56.   
  57.             // TODO: any other connection processing?  
  58.             return postProcessResult(result, connToUse, existingConnection);  
  59.         } finally {  
  60.   
  61.             if (enableTransactionSupport) {  
  62.                 RedisConnectionUtils.unbindConnection(factory);  
  63.             } else {  
  64.                 RedisConnectionUtils.releaseConnection(conn, factory);  
  65.             }  
  66.         }  
  67.     }  
  68.   
  69.     public  T execute(SessionCallback session) {  
  70.         Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");  
  71.         Assert.notNull(session, "Callback object must not be null");  
  72.   
  73.         RedisConnectionFactory factory = getConnectionFactory();  
  74.         // bind connection  
  75.         RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);  
  76.         try {  
  77.             return session.execute(this);  
  78.         } finally {  
  79.             RedisConnectionUtils.unbindConnection(factory);  
  80.         }  
  81.     }  
  82. …………省略若干创建连接代码…………  
  83.   
  84. …………以下是具体的操作,会调用回调方法…………  
  85.         protected List execRaw() {  
  86.         return execute(new RedisCallback>() {  
  87.             public List doInRedis(RedisConnection connection) throws DataAccessException {  
  88.                 return connection.exec();  
  89.             }  
  90.         });  
  91.     }  
  92.   
  93.     public void delete(K key) {  
  94.         final byte[] rawKey = rawKey(key);  
  95.   
  96.         execute(new RedisCallback() {  
  97.   
  98.             public Object doInRedis(RedisConnection connection) {  
  99.                 connection.del(rawKey);  
  100.                 return null;  
  101.             }  
  102.         }, true);  
  103.     }  
  104.   
  105.     public void delete(Collection keys) {  
  106.         if (CollectionUtils.isEmpty(keys)) {  
  107.             return;  
  108.         }  
  109.   
  110.         final byte[][] rawKeys = rawKeys(keys);  
  111.   
  112.         execute(new RedisCallback() {  
  113.   
  114.             public Object doInRedis(RedisConnection connection) {  
  115.                 connection.del(rawKeys);  
  116.                 return null;  
  117.             }  
  118.         }, true);  
  119.     }  
  120. }  
  121. 通过上面的示例,大家应该对 callback 机制有了一定的了解,最后,一言以蔽之 -- 如果你调用我,那么我就回调。

    你可能感兴趣的:(设计模式)