今天在学习Spring HibernateTemplate的过程中,碰到了好多回调,一直不太明白回调到底是啥意思,所以自己从网上看了下别人的例子学习了下:
<span style="font-size:18px;">回调机制
回调机制是一种常见的设计模型,他把工作流内的某个功能,按照约定的接口暴露给外部使用者,为外部使用者提供数据,
或要求外部使用者提供数据。
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。
同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;
回 调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;
异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,
会主动通知客户方(即调用客户方的接口)。
回调和异步调用的关系非常紧密:使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。
JAVA回调机制
回调是一种双向调用模式,被调用方在被调用时也会调用对方,If you call me, i will call back”。
一个比较经典的使用回调的方式:
class A实现接口InA ——背景1
class A中包含一个class B的引用b ——背景2
class B有一个参数为InA的方法test(InA a) ——背景3
A的对象a调用B的方法传入自己,test(a) ——这一步相当于you call me
然后b就可以在test方法中调用InA的方法 ——这一步相当于i call you back
先来看一个简单的例子,这个我在网上看到的,比较容易理解,不那么绕
/**
* 接口类(抽象类) 他需要在调用方法处被实现为一个匿名类,其中的postExec()就是回调方法
*/
public interface ICallBack {
//需要回调的方法
public void postExec();
}
/**
* 被调用的类,其中的setCallBack方法是被调用的方法
*/
public class FooBar {
private ICallBack callBack;
public void setCallBack(ICallBack callBack){
this.callBack=callBack;
doSth();
}
public void doSth(){
callBack.postExec();
}
}
/**
* 第二个类在测试类里面,是一个匿名类
*/
public class Test {
/* public static void main(String [] args){
FooBar foo=new FooBar();
foo.setCallBack(new ICallBack() {
@Override
public void postExec() {
System.out.println("在test类中实现但不能被Test的对象引用,而由FooBar对象调用");
}
});
}*/
}
上面的Test就是一个用于测试的调用者类,它通过main方法中实例化一个FooBar,并用实现的ICallBack的匿名
类作为参数传递给FooBar的被调用方法setCallBack,而在这个虚拟方法中,FooBar调用了匿名类的匿名类的postExec方法的动作,
这个动作就是回调(Callback)。
模拟Spring中HibernateTemplate回调机制的代码
/**
* 模拟Spring 中HibernateTemplate回调机制的代码
*/
public interface CallBack {
public void doCRUD();
}
/**
* Created by on 2014/11/19.
*/
public class HibernateTemplate {
public void execute(CallBack action){
getConnection();
action.doCRUD();
releaseConnection();
}
/**
* 我在这里的理解是:通过匿名内部类的方式实现了CallBack接口,执行execute方法时执行
* action.doCRUD(),这是就是回调了CallBack接口中的方法,这个方法已经在匿名内部类
* 里面是实现了,
*/
public void add(){
//1
execute(new CallBack() {
@Override
public void doCRUD() {
System.out.println("执行add操作");
}
});//2 1到2之间的代码相当于A类(这里的A类被隐藏)调用了B类。然后在执行execute时执行了 action.doCRUD();,
//所以相当于B类又回调了A类中的方法
}
public void getConnection(){
System.out.println("获得连接....");
}
public void releaseConnection(){
System.out.println("释放链接");
}
}
下面用回调的通俗表达方式来还原上面回调的过程:
interface CallBack{ //相当于接口InA
public void doCRUD();
}
public class A implements CallBack{//【背景1】
private B b;//【背景2】
public void doCRUD(){
System.out.println("执行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("释放连接...");
}
}
总之,相当于就是A调用B的方法,B再反过来调用A的方法.
为什么要使用回调呢?
像getConnection()是费时的操作,A想把这些交给B来完成,等B执行完之后再来通知A
这是使用了匿名内部类,匿名内部类的好处是:
匿名内部类是当做函数的参数传递给方法的,在这个方法中会用接口类型接受传入的匿名类
然后调用其方法,这是多态,原理其实就是实现了回调。
使用匿名内部类可以使语法看上去简洁。
什么是多态?
多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
多态有两种表现形式:重载和覆盖
首先说重载(overload),是发生在同一类中。与什么父类子类、继承毫无关系。
标识一个函数除了函数名外,还有函数的参数(个数和类型)。也就是说,一个类中可以有两个或更多的函数,叫同一个名字而他们的参数不同。
他们之间毫无关系,是不同的函数,只是可能他们的功能类似,所以才命名一样,增加可读性,仅此而已!
再说覆盖(override),是发生在子类中!也就是说必须有继承的情况下才有覆盖发生。
我们知道继承一个类,也就有了父类了全部方法,如果你感到哪个方法不爽,功能要变,那就把那个函数在子类中重新实现一遍。
这样再调用这个方法的时候,就是执行子类中的过程了。父类中的函数就被覆盖了。(当然,覆盖的时候函数名和参数要和父类中完全一样,不然你的方法对父类中的方法就不起任何作用,因为两者是两个函数,毫不关系)</span>