本文共 3260 字,大约阅读时间需要 10 分钟。
说起回调函数,必须要了解三个基本的东西:同步调用、回调和异步调用。
我们知道,在组织一个系统的时候我们通常将各个子模块之间预留一些接口以备调用,模块接口之间的调用可以分为上述三种方式:同步调用、回调和异步调用。
同步调用:也是最基本的调用,用更专业的术语来说叫做阻塞式调用,我们在写程序的时候通常使用的就是这种调用方式,当执行到某个地方之后,进入被调用函数执行,执行完成后再回调调用函数的地方接着向下执行,这种同步调用方式可能会耽误时间,占用资源等等,带来很多不便。
异步调用:则克服了同步调用的缺点,A程序调用B程序的时候,程序A、B可以同时执行,很明显,这样做节省了时间成本,也有其他的好处……,这里不再细说。
回调:也是这篇博文说的主要内容,函数回调是异步实现的基础,但是和异步又有所不同。
回调说的专业一点就是利用底层代码调用高层定义的子程序或实现方法,从效果上来说,不仅完成了接口原有功能的调用,还完成额外功能的定制。
用通俗例子讲:Boss杨给staff 程指派了任务,要staff程完成任务;(这个过程可以叫做函数的调用,高层定义指派底层代码完成任务)但是,Boss杨很忙,没办法 监督staff程,所以顺便跟staff程说完成任务之后要到他这里反馈结果,并且staff程也完成了任务,并到老板那里交代了任务结果。(staff程到Boss杨那里反馈结果的过程,可以比喻成函数的回调,即底层代码调用高层定义)。
代码化一点的例子:程序员A写了一段程序(程序a),其中预留有回调函数接口,并封装好了该程序。程序员B要让a调用自己的程序b中的一个方法,于是,他通过a的接口回调自己b中的方法。
#####(1)程序员A写的程序a,通常要对其进行封装,只给一个预留接口:
public class Caller { private MyCallInterface callInterface; public Caller() {} public void setCallFunc(MyCallInterface callInterface) { this.callInterface = callInterface; } public void call() { callInterface.printName(); }}
#####(2)预留回调函数接口,方便我们实现这个接口,在实现这个接口定义的方法里添加额外的功能:
public interface MyCallInterface { public void printName();}
#####(3)程序员B写程序b实现预留接口,以便让程序a通过预留接口回调程序b中的方法printName();
public class Client implements MyCallInterface { @Override public void printName() { System.out.println("This is the client printName method"); }}
#####(4)测试,通过封装的程序a(底层的)回调自己对预留接口的实现程序b(高层的):
public class Test { public static void main(String[] args) { Caller caller = new Caller(); caller.setCallFunc(new Client()); caller.call(); } }
**********另一种匿名类的回调方式,也是我们经常搞不懂觉得很高级的方式,看这里就明白了。省略掉实现类Client的定义,这里使用的时候直接定义紧接着实例化即可:
public class Test { public static void main(String[] args) { Caller caller = new Caller(); caller.setCallFunc(new MyCallInterface() { public void printName() { System.out.println("This is the client printName method"); } }); caller.call(); }}
在测试的时候,就是通过封装的底层代码程序a回调了高层的功能定义,在实现方法底层服务的同时,调用了高层的私人订制功能printName()。
***********************我是分割线************************************************************
上述是通过例子理解回调的含义,下面结合框架的使用说说我们在实际应用中怎么就用到回调了呢?
*******************人见人爱的分割线********************************************************
说起函数回调,很多人会感觉有点类似于面向接口编程,两者是有共同之处,但是又有所区别。
在经典的MVC三层架构中,一般我们会在业务层调用数据层,实现对数据的逻辑操作,不需要把业务层本身传递到数据层,这是一种典型的上层调用下层的关系。而我们在使用框架的时候习惯化利用框架提供的API,调用接口的实现方法。这就是我们常说的面向接口编程,封装并隐藏功能。
但函数回调则不同于面向接口编程,不仅是简单地调用框架提供的API那么简单。有时候,我们使用的框架所完成的执行功能不能满足需求,需要我们额外的对框架中的执行任务定制,这里可以说成私人订制,或者个性化定制,可是,我们总不能去随便修改框架里的东西吧,怎么办?
要解决这个问题,就需要用到函数的回调,在调用框架API的同时,还要定义一个类B实现框架提供的预留接口A(实现方法b),我们可以在这个b方法里添加自己想要执行的功能。理所当然,接下来我们会先调用框架的接口A,实例化自己的实现类B,然后调用框架类D提供的一个方法d完成执行功能。OK,表面上来说全部步骤完成了,貌似和我们自己添加的额外功能没有关系,实际上,在调用框架D类的d方法的时候,会自动将对象类B传递到框架里封装的类D,在我们调用D中的d方法完成框架提供的服务时,同时会通过预留接口API调用接受到的对B下的方法b,执行b里面的私人订制功能。这样,我们即调用了框架提供的服务,又完成了个性化功能定制。(事实上,框架和容器也确实提供了大量的回调接口,以供我们完成个性化定制)
如果上述解释还没有懂,可以结合上述代码例子理解:
上面的代码例子的意思:程序员A写了一段程序(程序a),其中预留有回调函数接口,并封装好了该程序。程序员B要让a调用自己的程序b中的一个方法,于是,他通过a的接口回调自己b中的方法,这是整个过程。
如果还没有懂,那么,请起身向左转走三十米,南面有道墙,可以选择直接撞上,也可以选择面壁思过。☹
Filter和Interceptor两者其中最大的一个区别是Filter是基于回调函数,需要容器的支持,没有容器是无法回调doFilter()方法,而Interceptor是基于Java的反射机制的,和容器无关。
看到此处,有没有将回调和反射搞混了?如果蒙圈了,参考一下
转载地址:http://gdlrb.baihongyu.com/