关闭

java中callback回调机制解析

标签: javacallback
1797人阅读 评论(0) 收藏 举报
分类:

我们首先来看下面一段代码,这段代码是我们使用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原则,怎么说了,上面代码中的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;
	
	@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);
			}
		});
	}
}
通过上面的代码,我们可以清晰的看到,将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 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机制有了一定的了解,最后,一言以蔽之--如果你调用我,那么我就回调。
0
1

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:180713次
    • 积分:3015
    • 等级:
    • 排名:第11736名
    • 原创:113篇
    • 转载:9篇
    • 译文:2篇
    • 评论:167条
    博客专栏
    最新评论