Spring中模板模式和回调模式(二)
来源:互联网 发布:gekka睡眠面膜 知乎 编辑:程序博客网 时间:2024/05/22 13:07
我们首先来看下面一段代码,这段代码是我们使用Jedis封装服务的一个实现:
- @Service
- public class JedisSpringDemo {
- @Resource(name = "shardedJedisPool")
- private ShardedJedisPool shardedJedisPool;
-
- public String set(String key, String value){
- ShardedJedis shardedJedis = null;
- try{
-
- shardedJedis = shardedJedisPool.getResource();
-
- 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{
-
- shardedJedis = shardedJedisPool.getResource();
-
- return action.doInRedis(shardedJedis);
-
- }catch (Exception e){
- System.out.println(e.getMessage());
- }finally {
- if(null != shardedJedis){
- shardedJedis.close();
- }
- }
- return null;
- }
-
-
-
-
- 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> {
-
-
- 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;
-
-
-
-
- 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");
-
- RedisConnectionFactory factory = getConnectionFactory();
- RedisConnection conn = null;
- try {
-
- if (enableTransactionSupport) {
-
-
- 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);
-
-
- if (pipeline && !pipelineStatus) {
- connToUse.closePipeline();
- }
-
-
- 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();
-
- 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机制有了一定的了解,最后,一言以蔽之--如果你调用我,那么我就回调。