Java中callback回调机制解析

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

@Service
public 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原则,怎么说了,上面代码中的try,catch,finally中的绝大部分代码都是雷同的,唯一不同的就是我们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;
    
    @Override
    public <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>() {
            @Override
            public String doInRedis(
                    ShardedJedis shardedJedis) {
                return shardedJedis.set(key, value);
            }
        });
    }
    
    public String get(final String key){
        return execute(new ConnectionCallback<String>(){
            @Override
            public String doInRedis(ShardedJedis shardedJedis) {
                return shardedJedis.get(key);
            }
        });
    }
}
通过上面的代码,我们可以清晰的看到,将try,catch,finally部分的公共代码都封装到了回调函数中,当调用具体方法的时候,再实现回调方法的具体业务逻辑即可,代码的复用率更高了。
如果大家对spring jdbc或者是spring data redis的源码研究过,就应该知道JdbcTemplate和RedisTemplate这两个类,这两个框架中用到了大量的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 pipeline
            if (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 connection
        RedisConnectionUtils.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机制有了一定的了解,最后,一言以蔽之--如果你调用我,那么我就回调。
转自:https://blog.csdn.net/liuchuanhong1/article/details/52353969 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值