回调函数是什么鬼, 回调函数干吗用,回调函数能够怎么用java
若是有过android开发经验,常常能够看到一些相似下面的代码android
Button Btn1 = (Button)findViewById(R.id.button1);//获取按钮资源 Btn1.setOnClickListener(new Button.OnClickListener(){//建立监听 public void onClick(View v) { String strTmp = "点击Button01"; Ev1.setText(strTmp); } });
上面注册的监听器其实就包含了这个回调的意味了。git
软件模块之间老是存在着必定的接口,从调用方式上,能够把他们分为三类:同步调用、回调和异步调用。
同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用; 回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口; 异步调用:一种相似消息或事件的机制,不过它的调用方向恰好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。github
回调和异步调用的关系很是紧密:使用回调来实现异步消息的注册,经过异步调用来实现消息的通知web
所谓回调,就是客户程序CLIENT调用服务程序SERVER中的某个函数SA(),而后SERVER又在某个时候反过来调用CLIENT中的某个函数mycallback(),对于CLIENT来讲,这个mycallback便叫作回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。apache
简单来讲,就是在调用一个组建的方法时,按照他的定义,注册一个咱们本身的方法,期待这个组建在某一个特意场景下调用咱们注册的方法,实现对应的功能api
上面简单的说明了什么是回调函数,那么怎么去设计一个回调函数呢?缓存
首先能够明确的是调用方要实现一个注册方法,被调用方提供一个功能,在某些特定的状况下,会调用注册方法java-web
如一个应用场景是:异步
如查询一条微博的点赞数,在db中保存了用户+微博的点赞映射关系,一般须要获取点赞数的时候,须要在db中count一下,计算点赞数,当点赞数不少的时候,改怎么办?每次都count一把? 性能开销难以接受
一个简单的方法是使用缓存,将点赞数保存在缓存中,每次获取点赞数都从缓存取,缓存没有命中的时候,才从db中count一把,并回写到缓存中
上面这个应用场景该如何设计成回调函数的形式呢?
通常为了通用性而言,CacheClient内部若是将缓存未命中查db的功能代码封装起来,会有什么问题?耦合过高,无法复用
so 形式话的结构以下:
CacheClient:
使用方 CountService:
看上面的描述能看懂么?卧槽,本身写的东西本身都看不大懂啊,果真仍是代码是王道,先看看代码,看一下是怎么玩的,而后在回过头去看一下上面的,效果会好不少
注册器相关类:
回调接口 CacheCallBackInterface
package com.mushroom.hui.common.register.callback; /** * 缓存未命中的回调函数 * Created by yihui on 16/4/5. */ public interface CacheCallBackInterface { String getKey(int id); int getExpire(); Object getObject(String key); }
注册接口 BaseRegister.java --> 为何要设计成接口?看CacheCient的时候会理解一点的
package com.mushroom.hui.common.register; import com.mushroom.hui.common.register.callback.CacheCallBackInterface; import java.util.HashMap; import java.util.Map; /** * Created by yihui on 16/4/5. */ public interface BaseRegister { Map<String, CacheCallBackInterface> map = new HashMap<>(); void register(String name, CacheCallBackInterface callback) throws Exception; Object exec(String name, int id) throws Exception; <T> T exec(String name, int id, Class<T> clz) throws Exception; }
CacheClient.java 对外提供的缓存客户端, 这个里面就实现了传说中的回调函数的使用
package com.mushroom.hui.common.cache; import com.mushroom.hui.common.cache.api.CacheInterface; import com.mushroom.hui.common.register.BaseRegister; import com.mushroom.hui.common.register.callback.CacheCallBackInterface; import com.mushroom.hui.common.register.exception.RegisterException; import org.apache.commons.lang.StringUtils; import javax.annotation.Resource; /** * Created by yihui on 16/4/5. */ public class CacheClient implements BaseRegister { private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CacheClient.class); @Resource(name = "cacheService") private CacheInterface methodCache; @Override public void register(String name, CacheCallBackInterface callback) throws Exception { if(StringUtils.isBlank(name)) { throw new IllegalArgumentException("key is empty!"); } if (map.containsKey(name)) { throw new RegisterException("this callback interface has already registered! name : " + name); } map.put(name, callback); } @Override public Object exec(String name, int id) throws Exception { return exec(name, id, Object.class); } @Override public <T> T exec(String name, int id, Class<T> clz) throws Exception { CacheCallBackInterface callBackInterface = map.get(name); if (callBackInterface == null) { throw new RegisterException("this callback interface has already registered! name : " + name); } String key = callBackInterface.getKey(id); Object obj = methodCache.getObject(key, clz); if (obj == null) { obj = callBackInterface.getObject(key); methodCache.setObject(key, obj, callBackInterface.getExpire()); } return (T) obj; } /** * 获取缓存的对象 * @param name 注册的回调函数name * @param id id * @param clz 返回的对象类型 * @param <T> * @return * @throws Exception */ public <T> T getObject(String name, int id, Class<T> clz) throws Exception { return exec(name, id, clz); } }
测试类中,对上面的功能进行测试,代码以下:
@Test public void testCacheClient() throws Exception { int id = 1002; String name = "cache_test"; // 注册回调函数 cacheClient.register(name, new CacheCallBackInterface(){ @Override public String getKey(int id) { return "test_" + id; } @Override public int getExpire() { return 30; } @Override public Object getObject(String key) { return key.length(); } }); Integer count = cacheClient.getObject(name, id ,Integer.class); logger.info("The count is : {}", count); cacheService.setObject("test_" + id, 1231234, 30); count = cacheClient.getObject(name, id, Integer.class); logger.info("The count is : {}", count); Thread.sleep(1000); count = cacheClient.getObject(name, id, Integer.class); logger.info("The count is : {}", count); }
写代码的时候仍是很清晰,知道应该怎么玩,而实际动手写这个流程的时候,仍是有不少问题啊,有一些地方竟然不清楚为何要那么设计,为何要那么玩,简直了,看来这一块了解的仍是不够透彻,后面把这一块吃透后,得重写一遍
最后给出代码的git地址 : https://github.com/liuyueyi/java-web-archetype/tree/demo
(这个工程主要是一个简单的java web demo实例工程,会逐渐的向其中添加一些公用的组件(工做中get到什么,就往里面塞什么东西))