Github原文
意圖
將Callback
(回調(diào))函數(shù)作為其他函數(shù)的參數(shù)傳入危队,以便在某個(gè)時(shí)間點(diǎn)調(diào)用這個(gè)方法。
解釋
Callback
(回調(diào))函數(shù)欺嗤,聽(tīng)起來(lái)高大上减余,其實(shí)跟其他函數(shù)沒(méi)有什么區(qū)別不脯,就是一個(gè)函數(shù)府怯。我們將它作為參數(shù)傳給另一個(gè)函數(shù),這在函數(shù)作為一等公民的JavaScript
中非常常見(jiàn)防楷,Java8
引入了Lambda
表達(dá)式后也可以做到牺丙。
舉個(gè)栗子,
IntStream.range(0, 3)
.forEach((i) -> System.out.println(String.format("%d號(hào)機(jī).", i)));
代碼中的forEach
括號(hào)中的代碼可以看作是它的參數(shù)复局,即是函數(shù)做了參數(shù)冲簿。
難以理解的是某個(gè)時(shí)間點(diǎn)這個(gè)詞。
為什么一定會(huì)在那個(gè)時(shí)間點(diǎn)亿昏?
誰(shuí)運(yùn)行了這段代碼峦剔?
答案是 ——函數(shù)不可能平白無(wú)故地自動(dòng)運(yùn)行,一定有誰(shuí)call
了它——其實(shí)就是更底層的代碼或是框架call
了它角钩。在接下來(lái)的代碼中吝沫,你會(huì)自己實(shí)現(xiàn)一份底層代碼。
UML
代碼
public interface Callback {
void call();
}
public abstract class Task {
/**
* 這就是我們自己的底層代碼
* 在這里递礼,‘某個(gè)時(shí)刻’代表了在execute()結(jié)束之后
*/
public final void executeWith(Callback callback) {
execute();
if (callback != null) callback.call();
}
protected abstract void execute();
}
public class SimpleTask extends Task {
protected void execute() {
System.out.println("Do some tasks before the callback method.");
}
}
public class App {
public static void main(String[] args) {
Task task = new SimpleTask();
Callback callback = new Callback() {
public void call() {
System.out.println("The callback method has been called!");
}
};
task.executeWith(callback);
}
}
擴(kuò)展
Spring
中的Bean
(即為被Spring
容器管理的POJO
)是有其生命周期的惨险。
而Spring
會(huì)提供生命周期回調(diào)函數(shù)的接口,以便我們可以在Bean
生命中的特定時(shí)間點(diǎn)做一些自定義的操作脊髓。示例代碼如下辫愉,
兩個(gè)接口
-
@PostConstruct
——接下來(lái)的方法會(huì)在Bean
創(chuàng)建以后執(zhí)行。 -
@PreDestroy
——接下來(lái)的方法會(huì)在Bean
被銷(xiāo)毀之前執(zhí)行将硝。
注意方法名本身不重要恭朗!
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyBean {
@PostConstruct
public void doYourTaskAfterConstruction() {
System.out.println("I have been constructed!");
}
@PreDestroy
public void doYourTaskBeforeDestroy() {
System.out.println("I will be destroyed, bye bye.");
}
}
然后定義一下元數(shù)據(jù)屏镊,表明上面的POJO
是個(gè)Bean
。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
最后用Spring
容器來(lái)加載元數(shù)據(jù)冀墨。
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
applicationContext.close();
}
}
運(yùn)行main函數(shù)的結(jié)果
I have been constructed!
I will be destroyed, bye bye.
結(jié)論
如何使用Callback
模式闸衫?
作為開(kāi)發(fā)者——我們可以規(guī)定在代碼執(zhí)行到某個(gè)時(shí)間點(diǎn)時(shí)涛贯,向外暴露出一些接口诽嘉,以便用戶(hù)自定義地實(shí)現(xiàn)這些接口。
比如上文中的executeWith(callback)
作為用戶(hù)——我們應(yīng)該知道開(kāi)發(fā)者為我們?cè)谀男r(shí)間點(diǎn)提供了接口弟翘。通過(guò)實(shí)現(xiàn)這些接口虫腋,我們可以保證代碼在約定好的時(shí)間點(diǎn)被運(yùn)行。
比如我們知道了在Spring
中稀余,@PostConstruct
和@PreDestroy
分別表示Bean
被創(chuàng)建之后和Bean
被銷(xiāo)毀之前這兩個(gè)時(shí)間點(diǎn)悦冀。