歡迎光臨我的個(gè)人博客:https://www.jelliclecat.cn/
一桑谍、回調(diào)(Callback)
回調(diào)(Callback)是編程中常用的一種編程模式酌呆。在程序運(yùn)行中继谚,當(dāng)程序執(zhí)行到某個(gè)特定的點(diǎn)或者達(dá)到某個(gè)條件后奏路,會(huì)執(zhí)行一個(gè)用戶自定義的方法阔蛉,用于執(zhí)行用戶自定義的在這個(gè)點(diǎn)執(zhí)行的邏輯弃舒,這種機(jī)制被稱為回調(diào)∽丛回調(diào)一般與一個(gè)事件綁定聋呢,當(dāng)事件發(fā)生后,會(huì)自動(dòng)調(diào)用這個(gè)方法颠区∠髅蹋回調(diào)在很多地方都有用到,例如毕莱,注冊(cè)一個(gè)監(jiān)聽器監(jiān)聽一個(gè)點(diǎn)擊事件器贩,在點(diǎn)擊發(fā)生之前這個(gè)監(jiān)聽器將阻塞颅夺,點(diǎn)擊事件發(fā)生后,就會(huì)自動(dòng)調(diào)用用戶自定義的運(yùn)行邏輯蛹稍,這種調(diào)用就是回調(diào)吧黄。回調(diào)的時(shí)機(jī)一般不是由用戶自己控制的稳摄,而是由框架或者事件控制的稚字,例如Spring中就大量使用回調(diào)機(jī)制(各種Template類饲宿,例如TransactionTemplate.java厦酬,JdbcTemplate.java等),這種回調(diào)機(jī)制類似一種hook瘫想,在模板執(zhí)行的邏輯中的某些點(diǎn)上仗阅,使用用戶自定義的邏輯。
TransactionTemplate.java中的execute方法国夜,這里刪除了一些邏輯:
@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
....
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
Java不能像C++那樣將一個(gè)函數(shù)作為參數(shù)傳入另一個(gè)函數(shù)减噪,但是Java可以傳入一個(gè)實(shí)例作為參數(shù),這樣车吹,我們可以將一個(gè)方法封裝到一個(gè)類里面筹裕,并使其竟可能的輕量級(jí)。當(dāng)我們需要傳入這樣的參數(shù)的時(shí)候窄驹,可以傳入一個(gè)匿名內(nèi)部類朝卒,在JDK8之后,你可以傳入一個(gè)Lambda表達(dá)式乐埠。
所以抗斤,Java的回調(diào)是通過匿名內(nèi)部類實(shí)現(xiàn)的。
以上面的Spring的TransactionTemplate.java類為例丈咐,其中封裝了回調(diào)方法的類是TransactionCallback<T> action
瑞眼,這里刪除了一些注釋:
@FunctionalInterface
public interface TransactionCallback<T> {
@Nullable
T doInTransaction(TransactionStatus status);
}
這里面只封裝了一個(gè)方法,這個(gè)方法就是回調(diào)方法棵逊,在execute方法里面調(diào)用:
result = action.doInTransaction(status);
@FunctionalInterface是一個(gè)編譯器檢查注解伤疙,用于提醒編譯器檢查這個(gè)接口是不是一個(gè)函數(shù)式接口。函數(shù)式接口意味著這個(gè)接口里面有且只有一個(gè)抽象方法辆影。例如一下兩個(gè)接口都是函數(shù)式接口:
@FunctionalInterface
public interface TransactionCallback<T> {
@Nullable
T doInTransaction(TransactionStatus status);
}
@FunctionalInterface
interface GreetingService{
void foo0(String message);
default void foo1(){}
default void foo1(){}
}
函數(shù)式接口意味著徒像,你在傳入一個(gè)這個(gè)接口類型的參數(shù)的時(shí)候,可以使用Lambda表達(dá)式秸歧。
當(dāng)然厨姚,你可以選擇不使用@FunctionalInterface,也沒有任何影響键菱,因?yàn)樗淖饔脙H僅是提醒編譯器去檢查一下谬墙。
二今布、自己寫一個(gè)回調(diào)
@FunctionalInterface
interface Callback {
void do();
}
class Caller {
public void call(Callback callback) {
System.out.println("before");
callback.do();
System.out.println("after");
}
}
public static void main(String[] args) {
Caller caller = new Caller();
caller.call(() -> System.out.println("doing"));
}
// 結(jié)果:
before
doing
after
是不是很簡(jiǎn)單~
當(dāng)然,上面的例子只是一個(gè)簡(jiǎn)單的示例拭抬,回調(diào)有一個(gè)非常好的好處是部默,可以在某個(gè)事件發(fā)生的時(shí)候再調(diào)用我們定義的回調(diào):
@FunctionalInterface
interface Callback {
void do();
}
class Event {
private String name;
public String getName() { return name; }
}
class Caller {
private BlockingQueue<Event> queue;
public void setQueue(BlockingQueue<Event> queue) { this.queue = queue; }
public void call(Callback callback) {
Event event = queue.take();
if("click".equals(event.getName()))
callback.do();
}
}
public static void main(String[] args) {
Caller caller = new Caller();
caller.call(() -> System.out.println("click done"));
}
假設(shè)在其他地方會(huì)不定時(shí)的產(chǎn)生各種event,這些event會(huì)被一個(gè)producer加入到queue中造虎,我們的方法中傅蹂,會(huì)使用Event event = queue.take()去阻塞的獲取各種產(chǎn)生的event,當(dāng)event的類型是"click"時(shí)算凿,調(diào)用Callback中定義的方法份蝴,打印"click done"。這種實(shí)現(xiàn)方式就是一種最原始的監(jiān)聽器(Listener)編程模式(貌似不是設(shè)計(jì)模式氓轰,待確認(rèn))婚夫。
歡迎交流~
歡迎光臨我的個(gè)人博客:https://www.jelliclecat.cn/