java中callback回调机制解析

来源:互联网 发布:linux图灵书籍 编辑:程序博客网 时间:2024/06/05 10:23

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

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

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

public interface RedisOperations {<T> T execute(ConnectionCallback<T> action);   String set(final String key, final String value);   String get(final String key);}
其次,定义连接Redis服务器的回调接口,代码如下:
public interface ConnectionCallback<T> {T doInRedis(ShardedJedis shardedJedis);}
最后定义具体的操作服务类,代码如下:
@Service("redisTemplate")public class RedisTemplate implements RedisOperations{@Resource(name = "shardedJedisPool")private ShardedJedisPool shardedJedisPool;@Overridepublic <T> T execute(ConnectionCallback<T> action) {ShardedJedis shardedJedis = null;try{// 从连接池中获取jedis分片对象shardedJedis = shardedJedisPool.getResource();return action.doInRedis(shardedJedis);}catch (Exception e){System.out.println(e.getMessage());}finally {if(null != shardedJedis){shardedJedis.close();}}return null;}   /** * attention:真正封装的方法,非常的简洁干脆 */public String set(final String key, final String value){return execute(new ConnectionCallback<String>() {@Overridepublic String doInRedis(ShardedJedis shardedJedis) {return shardedJedis.set(key, value);}});}public String get(final String key){return execute(new ConnectionCallback<String>(){@Overridepublic String doInRedis(ShardedJedis shardedJedis) {return shardedJedis.get(key);}});}}
通过上面的代码,我们可以清晰的看到,将trycatchfinally部分的公共代码都封装到了回调函数中,当调用具体方法的时候,再实现回调方法的具体业务逻辑即可,代码的复用率更高了。

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

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

public interface RedisCallback<T> {    T doInRedis(RedisConnection connection) throws DataAccessException;}
其次,定义操作方法,代码如下:
public interface RedisOperations<K, V> {    <T> T execute(RedisCallback<T> action);    <T> T execute(SessionCallback<T> session);   …………省略若干方法…………}
最后,回调机制的实现RedisTemplate类,代码如下:
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V> {// 以下定义的是Redis支持的操作类型,例如SetOperations就是用来操作Set类型的,由于Redis支持的操作类型比较多,所以将每种操作类型都抽象成一个具体的操作类private ValueOperations<K, V> valueOps;private ListOperations<K, V> listOps;private SetOperations<K, V> setOps;private ZSetOperations<K, V> zSetOps;private HyperLogLogOperations<K, V> hllOps;/** * Constructs a new <code>RedisTemplate</code> instance. */public RedisTemplate() {}public <T> T execute(RedisCallback<T> action) {return execute(action, isExposeConnection());}public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {return execute(action, exposeConnection, false);}public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");Assert.notNull(action, "Callback object must not be null");       // 获取Redis服务器的连接工厂RedisConnectionFactory factory = getConnectionFactory();RedisConnection conn = null;try {if (enableTransactionSupport) {// only bind resources in case of potential transaction synchronization             // 如果开启了事物的话,需将连接绑定到事物上conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);} else {            // 获取连接conn = RedisConnectionUtils.getConnection(factory);}boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);RedisConnection connToUse = preProcessConnection(conn, existingConnection);boolean pipelineStatus = connToUse.isPipelined();if (pipeline && !pipelineStatus) {connToUse.openPipeline();}RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));T result = action.doInRedis(connToExpose);// close pipelineif (pipeline && !pipelineStatus) {connToUse.closePipeline();}// TODO: any other connection processing?return postProcessResult(result, connToUse, existingConnection);} finally {if (enableTransactionSupport) {RedisConnectionUtils.unbindConnection(factory);} else {RedisConnectionUtils.releaseConnection(conn, factory);}}}public <T> T execute(SessionCallback<T> session) {Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");Assert.notNull(session, "Callback object must not be null");RedisConnectionFactory factory = getConnectionFactory();// bind connectionRedisConnectionUtils.bindConnection(factory, enableTransactionSupport);try {return session.execute(this);} finally {RedisConnectionUtils.unbindConnection(factory);}}…………省略若干创建连接代码……………………以下是具体的操作,会调用回调方法…………    protected List<Object> execRaw() {return execute(new RedisCallback<List<Object>>() {public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {return connection.exec();}});}public void delete(K key) {final byte[] rawKey = rawKey(key);execute(new RedisCallback<Object>() {public Object doInRedis(RedisConnection connection) {connection.del(rawKey);return null;}}, true);}public void delete(Collection<K> keys) {if (CollectionUtils.isEmpty(keys)) {return;}final byte[][] rawKeys = rawKeys(keys);execute(new RedisCallback<Object>() {public Object doInRedis(RedisConnection connection) {connection.del(rawKeys);return null;}}, true);}}
通过上面的示例,大家应该对callback机制有了一定的了解,最后,一言以蔽之--如果你调用我,那么我就回调。
0 1