关闭

java中callback回调机制解析

标签: javacallback
3269人阅读 评论(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网站的观点或立场

Spring Data Redis 二:RedisTemplate实现事物问题剖析和解决

Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,Redis对事物支持不会很复杂,当一个客服端连接Redis服务时,发出了MULTI命令时,这个连接会进入事物,在执行MULTI命令之后...
  • congcong68
  • congcong68
  • 2016-10-04 22:33
  • 8086

Spring集成Redis使用RedisTemplate

1、POM: junit junit 4.8.2 test org.springframework.data spring-data-redis 1.0.2.RELEASE...
  • yk123666
  • yk123666
  • 2016-12-16 15:20
  • 4715

Redis源码解析:19Hiredis异步API代码解析

Hiredis中的异步API函数需要与事件库(libevent,libev, ev)一起工作。因为事件循环的机制,异步环境中的命令是自动管道化的。因为命令是异步发送的,因此发送命令时,必要情况下,需要...
  • gqtcgq
  • gqtcgq
  • 2016-05-12 09:40
  • 5613

Redis API 必杀解读(一):引入RedisTemplate

题记在工作和学习中啊,比如说JAVA开发,要使用Redis,首先要引入一个RedisTemplate类// // Source code recreated from a .class file by...
  • wd2014610
  • wd2014610
  • 2017-08-18 16:20
  • 2032

Sping Data Redis 使用事务时,不关闭连接的问题

项目中使用到了Redis,最后决定用spring-data-redis来开发,配置好连接池,进入使用,似乎一切正常。  配置了两块redis,一个专门做读,一个专门做些, 配置的XML文件如下,这是一...
  • LiuHanFanShuang
  • LiuHanFanShuang
  • 2016-08-06 15:10
  • 7697

java "回调【 Callback 】" 的理解和使用<借鉴>

对callback,暂时的理解是:A对象调用B接口的b方法,b方法又反过来调用A对象中的c方法。  A调用B接口时把自身给B接口,至于怎么处理,由B的实现类去做,不关A的事。  写了个例子...
  • itm_hadf
  • itm_hadf
  • 2012-05-04 21:51
  • 3043

Java中实现CallBack功能

在很多场景,作为开发都会想到,在执行完毕一个任务的时候,能执行一个callback函数是多么好的事情。 现在模拟一下这个情景: 定义三个类。分别是主函数类。callback函数的接口类。业务处理类。在...
  • imzoer
  • imzoer
  • 2013-07-23 14:51
  • 20000

关于spring session redis共享session的一个坑

关于spring session redis共享session的一个坑,这两天写spring session redis发现几个小问题,挨个絮叨絮叨
  • zdsdiablo
  • zdsdiablo
  • 2015-12-29 17:44
  • 33094

一个经典例子让你彻彻底底理解java回调机制

以前不理解什么叫回调,天天听人家说加一个回调方法啥的,心里想我草,什么叫回调方法啊?然后自己就在网上找啊找啊找,找了很多也不是很明白,现在知道了,所谓回调:就是A类中调用B类中的某个方法C,然后B类中...
  • xiaanming
  • xiaanming
  • 2013-03-21 23:43
  • 261273

Java回调函数之callback接口

资料一: 首先说说什么叫回调函数?     在WINDOWS中,程序员想让系统DLL调用自己编写的一个方法,于是利用DLL当中回调函数(CALLBACK)的接口来编写程序,使它调用...
  • fjseryi
  • fjseryi
  • 2016-01-07 08:45
  • 3521
    个人资料
    • 访问:498090次
    • 积分:5873
    • 等级:
    • 排名:第5098名
    • 原创:128篇
    • 转载:9篇
    • 译文:2篇
    • 评论:359条
    博客专栏
    最新评论