1. 程式人生 > >Java回撥機制解析

Java回撥機制解析

模組之間總是存在這一定的介面,從呼叫方式上看,可以分為三類:同步呼叫、回撥和非同步呼叫。同步呼叫是一種阻塞式呼叫,也是我們在寫程式中經常使用的;回撥是一種雙向的呼叫模式,也就是說,被呼叫的介面被呼叫時也會呼叫對方的介面,這句話可能有點繞,等文章後面舉例說明;非同步呼叫是一種類似訊息或事件的機制,解決了同步阻塞的問題,舉例來講:A通知B後,他們各走各的路,互不影響,不用像同步呼叫那樣,A通知B後,非得等到B走完後,A才繼續走。回撥是非同步的基本,因此下面著重說回撥機制。

我們暫且不討論回撥的一些名詞和執行機制,首先說為什麼會存在回撥這樣一種呼叫?同步和非同步機制的出現不必多說,大家心知肚明,那回調機制為什麼會出現呢?在我們現實生活中,有如下這樣場景:有一位老闆(上層模組)很忙,他沒有時間盯著員工(下層模組)幹活,然後他告訴自己的僱員,幹完當前這些事情後,告訴他幹活的結果。這個例子其實是一個回撥
+非同步的例子,再舉一個例子,A程式設計師寫了一段程式a,其中預留了回撥函式介面,並封裝好了該程式,程式設計師Ba呼叫自己的程式b中的一個方法,於是,他通過a中的介面回撥自己b中的方法,到這裡你可能似懂非懂了,後面會繼續說明回撥的出現原因。接下來我們把上面例子變成程式碼,看到網上很多人最後搞混了非同步和回撥,因此例子中不加入非同步呼叫。(注意:回撥可不是解決什麼呼叫時間過長問題,那是非同步!

首先建立一個回撥介面,讓老闆得告知幹完活如何找到他的方式:留下老闆辦公室地址:

package net.easyway.test;

/**
 * 此介面為聯絡的方式,不論是電話號碼還是聯絡地址,作為
 * 老闆都必須要實現此介面
 * @author Administrator
 *
 */
public interface CallBackInterface {

	public void execute();
}

建立回撥物件,就是老闆本人,因為員工幹完活後要給他打電話,因此老闆必須實現回撥介面,不然員工去哪裡找老闆?

package net.easyway.test;

/**
 * 老闆是作為上層應用身份出現的,下層應用(員工)是不知道
 * 有哪些方法,因此他想被下層應用(員工)呼叫必須實現此介面
 * @author Administrator
 *
 */
public class Boss implements CallBackInterface {
	
	@Override
	public void execute() {
		System.out.println("收到了!!" + System.currentTimeMillis());
		
	}
}

建立控制類,也就是員工物件,他必須持有老闆的地址(回撥介面),即使老闆換了一茬又一茬,辦公室不變,總能找到對應的老闆。

package net.easyway.test;

/**
 * 員工類,必須要記住,這是一個底層類,底層是不瞭解上層服務的
 * @author Administrator
 *
 */
public class Employee {

	private CallBackInterface callBack = null;
	
	//告訴老闆的聯絡方式,也就是註冊
	public void setCallBack(CallBackInterface callBack){
		this.callBack = callBack;
	}
	
	//工人幹活
	public void doSome(){
		//1.開始幹活了
		for(int i=0;i<10;i++){
			System.out.println("第【" + i + "】事情幹完了!");
		}
		
		//2.告訴老闆幹完了
		callBack.execute();
	}
}

測試類程式碼:

package net.easyway.test;

public class Client {

	public static void main(String[] args) {
		
		
		Employee emp = new Employee();
		
		//將回調物件(上層物件)傳入,註冊
		emp.setCallBack(new Boss());
		
		//開啟控制器物件執行
		emp.doSome();
	}

}

上面這個例子,大家可以和程式設計師A和程式設計師B的那個例子結合對照下。

看了上面的例子,有的人可能認為,這不是面向介面的程式設計嗎?怎麼會是回撥,你再好好想想,咱們面向介面的程式設計的呼叫關係?在三層中,當業務層呼叫資料層時,是不需要把業務層自身傳遞到資料層的,並且這是一種上層呼叫下層的關係,比如我們在用框架的時候,一般直接呼叫框架提供的API就可以了,但回撥不同,當框架不能滿足需求,我們想讓框架來呼叫自己的類方法,怎麼做呢?總不至於去修改框架吧。許多優秀的框架提幾乎都供了相關的介面,我們只需要實現相關介面,即可完成了註冊,然後在合適的時候讓框架來呼叫我們自己的類,還記不記得我們在使用Struts時,當我們編寫Action時,就需要繼承Action類,然後實現execute()方法,在execute()方法中寫咱們自己的業務邏輯程式碼,完成對使用者請求的處理。由此可以猜測,框架和容器中會提供大量的回撥介面,以滿足個性化的定製。

不知道上面這個例子懂了沒有?我們現在可以想象FilterInterceptor的區別了,這兩者其中最大的一個區別是Filter是基於回撥函式,需要容器的支援,沒有容器是無法回撥doFilter()方法,而Interceptor是基於Java的反射機制的,和容器無關。那到此是否又將反射和回撥搞混了呢?請見我講Java----AOP(代理模式)》。

總之,要明確的一點是,首先要搞清回撥函數出現的原因,也就是適用場景,才能搞清楚回撥機制,不然事倍功半。

最後,再舉一例,為了使我們寫的函式接近完美,就把一部分功能外包給別人,讓別人個性化定製,至於別人怎麼實現不管,我唯一要做的就是定義好相關介面,這一設計允許了底層程式碼呼叫高層定義的子程式,增強程式靈活性,和反射有著異曲同工之妙,這才是回撥的真正原因!

用一段話來總結下回調:上層模組封裝時,很難預料下層模組會如何實現,因此,上層模組只需定義好自己需要但不能預料的介面(也就是回撥介面),當下層模組呼叫上層模組時,根據當前需要的實現回撥介面,並通過註冊或引數方式傳入上層模組即可,這樣就實現下層呼叫上層,並且上層還能根據傳入的引用來呼叫下層的具體實現,將程式的靈活性大大的增加了。本打算以spring jdbctemplate作為例項,但裡面涉及到不止回撥,還有模板模式等,怕理解困難就沒有采用,感興趣的讀者可自行檢視相關原始碼,也是回撥+內部類的很好示例。

關於事件寫的一篇很不錯的文章,貼出來:http://blog.csdn.net/coolmeme/article/details/6060787