qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請訪問 http://qaseven.github.io/

          深入淺出Java回調(diào)機制

           前幾天看了一下Spring的部分源碼,發(fā)現(xiàn)回調(diào)機制被大量使用,覺得有必要把Java回調(diào)機制的理解歸納總結(jié)一下,以方便在研究類似于Spring源碼這樣的代碼時能更加得心應(yīng)手。
            注:本文不想扯很多拗口的話來充場面,我的目的是希望以最簡明扼要的語言將Java回調(diào)的大概機制說清楚。好了,言歸正傳。
            一句話,回調(diào)是一種雙向調(diào)用模式,什么意思呢,就是說,被調(diào)用方在被調(diào)用時也會調(diào)用對方,這就叫回調(diào)。“If you call me, i will call back”。
            不理解?沒關(guān)系,先看看這個可以說比較經(jīng)典的使用回調(diào)的方式:
            class A實現(xiàn)接口InA ——背景1
            class A中包含一個class B的引用b ——背景2
            class B有一個參數(shù)為InA的方法test(InA a) ——背景3
            A的對象a調(diào)用B的方法傳入自己,test(a) ——這一步相當(dāng)于you call me
            然后b就可以在test方法中調(diào)用InA的方法 ——這一步相當(dāng)于i call you back
            是不是清晰一點了?下面再來看一個完全符合這個方式模板的例子
            (PS:這個例子來源于網(wǎng)絡(luò),由于這個例子表現(xiàn)的功能極度拉風(fēng),令我感覺想想出一個超越它的例子確實比較困難,所以直接搬過來)
          //相當(dāng)于接口InA
          public interface BoomWTC{
          //獲得拉登的決定
          public benLaDengDecide();
          // 執(zhí)行轟炸世貿(mào)
          public void boom();
          }
          //相當(dāng)于class A
          public class At$911 implements BoomWTC{//相當(dāng)于【背景1】
          private boolean decide;
          private TerroristAttack ta;//相當(dāng)于【背景2】
          public At$911(){
          Date now=new Date();
          SimpleDateFormat myFmt1=new SimpleDateFormat("yy/MM/dd HH:mm");
          this.dicede= myFmt.format(dt).equals("01/09/11 09:44");
          this.ta=new TerroristAttack();
          }
          //獲得拉登的決定
          public boolean benLaDengDecide(){
          return decide;
          }
          // 執(zhí)行轟炸世貿(mào)
          public void boom(){
          ta.attack(new At$911);//class A調(diào)用class B的方法傳入自己的對象,相當(dāng)于【you call me】
          }
          }
          //相當(dāng)于class B
          public class TerroristAttack{
          public TerroristAttack(){
          }
          public attack(BoomWTC bmw){——這相當(dāng)于【背景3】
          if(bmw.benLaDengDecide()){//class B在方法中回調(diào)class A的方法,相當(dāng)于【i call you back】
          //let's go.........
          }
          }
          }
            現(xiàn)在應(yīng)該對回調(diào)有一點概念了吧。
          可是問題來了,對于上面這個例子來說,看不出用回調(diào)有什么好處,直接在調(diào)用方法不就可以了,為什么要使用回調(diào)呢?
            事實上,很多需要進(jìn)行回調(diào)的操作是比較費時的,被調(diào)用者進(jìn)行費時操作,然后操作完之后將結(jié)果回調(diào)給調(diào)用者??催@樣一個例子:
          //模擬Spring中HibernateTemplate回調(diào)機制的代碼
          interface CallBack{
          public void doCRUD();
          }
          public class HibernateTemplate {
          public void execute(CallBack action){
          getConnection();
          action.doCRUD();
          releaseConnection();
          }
          public void add(){
          execute(new CallBack(){
          public void doCRUD(){
          System.out.println("執(zhí)行add操作...");
          }
          });
          }
          public void getConnection(){
          System.out.println("獲得連接...");
          }
          public void releaseConnection(){
          System.out.println("釋放連接...");
          }
          }
            可能上面這個例子你不能一眼看出個所以然來,因為其實這里A是作為一個內(nèi)部匿名類存在的。好,不要急,讓我們把這個例子來重構(gòu)一下:
          interface CallBack{   //相當(dāng)于接口InA
          public void doCRUD();
          }
          public class A implements CallBack{//【背景1】
          private B b;//【背景2】
          public void doCRUD(){
          System.out.println("執(zhí)行add操作...");
          }
          public void add(){
          b.execute(new A());//【you call me】
          }
          }
          public class B{
          public void execute(CallBack action){  //【背景3】
          getConnection();
          action.doCRUD();  //【i call you back】
          releaseConnection();
          }
          public void getConnection(){
          System.out.println("獲得連接...");
          }
          public void releaseConnection(){
          System.out.println("釋放連接...");
          }
          }
            好了,現(xiàn)在就明白多了吧,完全可以轉(zhuǎn)化為上面所說的回調(diào)使用方式的模板。
            現(xiàn)在在來看看為什么要使用回調(diào),取得連接getConnection();是費時操作,A希望由B來進(jìn)行這個費時的操作,執(zhí)行完了之后通知A即可(即所謂的i call you back)。這就是這里使用回調(diào)的原因。
            在網(wǎng)上看到了一個比喻,覺得很形象,這里借用一下:
            你有一個復(fù)雜的問題解決不了,打電話給你的同學(xué),你的同學(xué)說可以解決這個問題,但是需要一些時間,那么你不可能一直拿著電話在那里等,你會把你的電話號碼告訴他,讓他解決之后打電話通知你?;卣{(diào)就是體現(xiàn)在你的同學(xué)又反過來撥打你的號碼。
            結(jié)合到前面所分析的,你打電話給你同學(xué)就是【you call me】,你同學(xué)解決完之后打電話給你就是【i call you back】。
            怎么樣,現(xiàn)在理解了吧?
            ---------------------------------以下為更新----------------------------------
            看了有些朋友的回帖,我又思考了一下,感覺自己之前對回調(diào)作用的理解的確存在偏差。
            下面把自己整理之后的想法共享一下,如果有錯誤希望指出!多謝!
            先說上面這段代碼,本來完全可以用模板模式來進(jìn)行實現(xiàn):
          public abstract class B{
          public void execute(){
          getConnection();
          doCRUD();
          releaseConnection();
          }
          public abstract void doCRUD();
          public void getConnection(){
          System.out.println("獲得連接...");
          }
          public void releaseConnection(){
          System.out.println("釋放連接...");
          }
          }
          public class A extends B{
          public void doCRUD(){
          System.out.println("執(zhí)行add操作...");
          }
          public void add(){
          doCRUD();
          }
          }
          public class C extends B{
          public void doCRUD(){
          System.out.println("執(zhí)行delete操作...");
          }
          public void delete(){
          doCRUD();
          }
          }
          如果改為回調(diào)實現(xiàn)是這樣的:
          interface CallBack{
          public void doCRUD();
          }
          public class HibernateTemplate {
          public void execute(CallBack action){
          getConnection();
          action.doCRUD();
          releaseConnection();
          }
          public void add(){
          execute(new CallBack(){
          public void doCRUD(){
          System.out.println("執(zhí)行add操作...");
          }
          });
          }
          public void delete(){
          execute(new CallBack(){
          public void doCRUD(){
          System.out.println("執(zhí)行delete操作...");
          }
          });
          }
          public void getConnection(){
          System.out.println("獲得連接...");
          }
          public void releaseConnection(){
          System.out.println("釋放連接...");
          }
          }
            可見摒棄了繼承抽象類方式的回調(diào)方式更加簡便靈活。不需要為了實現(xiàn)抽象方法而總是繼承抽象類,而是只需要通過回調(diào)來增加一個方法即可,更加的直觀簡潔靈活。這算是回調(diào)的好處之一。
            下面再給出一個關(guān)于利用回調(diào)配合異步調(diào)用的很不錯的例子
            回調(diào)接口:
          public interface CallBack {
          /**
          * 執(zhí)行回調(diào)方法
          * @param objects   將處理后的結(jié)果作為參數(shù)返回給回調(diào)方法
          */
          public void execute(Object... objects );
          }
          消息的發(fā)送者:
          /**
          * 這個類相當(dāng)于你自己
          */
          public class Local implements CallBack,Runnable{
          private Remote remote;
          /**
          * 發(fā)送出去的消息
          */
          private String message;
          public Local(Remote remote, String message) {
          super();
          this.remote = remote;
          this.message = message;
          }
          /**
          * 發(fā)送消息
          */
          public void sendMessage()
          {
          /**當(dāng)前線程的名稱**/
          System.out.println(Thread.currentThread().getName());
          /**創(chuàng)建一個新的線程發(fā)送消息**/
          Thread thread = new Thread(this);
          thread.start();
          /**當(dāng)前線程繼續(xù)執(zhí)行**/
          System.out.println("Message has been sent by Local~!");
          }
          /**
          * 發(fā)送消息后的回調(diào)函數(shù)
          */
          public void execute(Object... objects ) {
          /**打印返回的消息**/
          System.out.println(objects[0]);
          /**打印發(fā)送消息的線程名稱**/
          System.out.println(Thread.currentThread().getName());
          /**中斷發(fā)送消息的線程**/
          Thread.interrupted();
          }
          public static void main(String[] args)
          {
          Local local = new Local(new Remote(),"Hello");
          local.sendMessage();
          }
          public void run() {
          remote.executeMessage(message, this);  //這相當(dāng)于給同學(xué)打電話,打完電話之后,這個線程就可以去做其他事情了,只不過等到你的同學(xué)打回電話給你的時候你要做出響應(yīng)
          }
          }
            消息的接收者:
          /**
          * 這個類相當(dāng)于你的同學(xué)
          */
          public class Remote {
          /**
          * 處理消息
          * @param msg   接收的消息
          * @param callBack  回調(diào)函數(shù)處理類
          */
          public void executeMessage(String msg,CallBack callBack)
          {
          /**模擬遠(yuǎn)程類正在處理其他事情,可能需要花費許多時間**/
          for(int i=0;i<1000000000;i++)
          {
          }
          /**處理完其他事情,現(xiàn)在來處理消息**/
          System.out.println(msg);
          System.out.println("I hava executed the message by Local");
          /**執(zhí)行回調(diào)**/
          callBack.execute(new String[]{"Nice to meet you~!"});  //這相當(dāng)于同學(xué)執(zhí)行完之后打電話給你
          }
          }
            由上面這個例子可見,回調(diào)可以作為異步調(diào)用的基礎(chǔ)來實現(xiàn)異步調(diào)用。

          posted on 2014-07-15 10:28 順其自然EVO 閱讀(152) 評論(0)  編輯  收藏 所屬分類: 測試學(xué)習(xí)專欄

          <2014年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 花莲市| 曲靖市| 岱山县| 墨脱县| 菏泽市| 清河县| 隆安县| 阿合奇县| 惠安县| 台南市| 普陀区| 淮阳县| 民权县| 光山县| 启东市| 彭水| 铜川市| 庆阳市| 镇康县| 宁陵县| 长岛县| 荆门市| 辛集市| 巢湖市| 尤溪县| 措美县| 崇义县| 固阳县| 白水县| 绥棱县| 行唐县| 修水县| 华宁县| 高平市| 罗江县| 高尔夫| 健康| 古田县| 云霄县| 巨野县| 马关县|