回調函數是什麼鬼, 回調函數幹嗎用,回調函數能夠怎麼用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到什麼,就往裏面塞什麼東西))