最近看spring的JDBCTemplete的模板方式调用时,对模板和回调产生了浓厚兴趣,查询了一些资料,做一些总结。

回调函数:

  所谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数。回调函数是一个工作流的一部分,由工作流来决定函数的调用(回调)时机。一般说来,C不会自己调用B,C提供B的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的B姓甚名谁,所以S会约定B的接口规范(函数原型),然后由C提前通过S的一个函数R告诉S自己将要使用B函数,这个过程称为回调函数的注册,R称为注册函数。Web Service以及Java 的RMI都用到回调机制,可以访问远程服务器程序。回调函数包含下面几个特性:

1、属于工作流的一个部分;

2、必须按照工作流指定的调用约定来申明(定义);

3、他的调用时机由工作流决定,回调函数的实现者不能直接调用回调函数来实现工作流的功能;

回调机制:

回调机制是一种常见的设计模型,他把工作流内的某个功能,按照约定的接口暴露给外部使用者,为外部使用者提供数据,或要求外部使用者提供数据。

java回调机制:

软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。

同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;

回    调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;

异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。

回调和异步调用的关系非常紧密:使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。

回调实例

1、回调接口

 public interface Callback {
String callBack();
}

2、调用者

 public class Another {
private Callback callback;
//调用实现类的方法
public void setCallback(Callback callback) {
this.callback = callback;
}
//业务需要的时候,通过委派,来调用实现类的具体方法
public void doCallback(){
System.out.println(callback.callBack());
}
}

3、测试回调函数

 public class TestCallcack {
public static void main(String[] args) {
//创建调用者的实现类
Another another = new Another();
//将回掉接口注册到实现类中
another.setCallback(new Callback() {
@Override
public String callBack() {
return "you are a pig";
}
});
//执行回调函数
another.doCallback();
}
}

回调方法的使用通常发生在“java接口”和“抽象类”的使用过程中。模板方法设计模式就使用方法回调的机制,该模式首先定义特定的步骤的算法骨架,而将一些步骤延迟到子类中去实现的设计模式。模板方法设计模式使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

模板方式设计模式的适用性:

  1、一次性实现一个算法的不变部分,并将可变的算法留给子类来实现。

  2、各子类中公共的行为应该被提取出来并集中一个公共父类中以避免代码重复。

  3、可以控制子类扩展。

模板实例:

抽象模板方法类:

 public abstract class AbstractSup {
//需要子类实现的方法
public abstract void print();
//模板方法
public void doPrint(){
System.out.println("执行模板方法");
for (int i = 0; i < 3; i++) {
print();
}
}
}

子类实现模板方式类:

 public class SubClass extends AbstractSup{
@Override
public void print() {
System.out.println("子类的实现方法");
} }

模板方法测试类:

 public class TempleteTest {
public static void main(String[] args) {
SubClass subClass = new SubClass();
subClass.print();
subClass.doPrint();
}
}

下面深入介绍下spring模板方法的使用,以JdbcTemplete为例,详细说明模板模式和回调机制的使用。

首先看一下经典的JDBC编程的例子:

 public List<User> query() {  
  
    List<User> userList = new ArrayList<User>();  
    String sql = "select * from User";  
  
    Connection con = null;  
    PreparedStatement pst = null;  
    ResultSet rs = null;  
    try {  
        con = HsqldbUtil.getConnection();  
       pst = con.prepareStatement(sql);  
        rs = pst.executeQuery();  
  
        User user = null;  
        while (rs.next()) {  
  
            user = new User();  
            user.setId(rs.getInt("id"));  
            user.setUserName(rs.getString("user_name"));  
            user.setBirth(rs.getDate("birth"));  
            user.setCreateDate(rs.getDate("create_date"));  
           userList.add(user);  
        }  
  
  
    } catch (SQLException e) {  
        e.printStackTrace();  
    }finally{  
        if(rs != null){  
            try {  
                rs.close();  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }  
        try {  
            pst.close();  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
        try {  
           if(!con.isClosed()){  
                try {  
                    con.close();  
              } catch (SQLException e) {  
                    e.printStackTrace();  
                }  
            }  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
          
    }  
    return userList;  
}  

一个简单的查询,就要做这么一大堆事情,而且还要处理异常,我们不防来梳理一下: 
1、获取connection 
2、获取statement 
3、获取resultset 
4、遍历resultset并封装成集合 
5、依次关闭connection,statement,resultset,而且还要考虑各种异常等等。

如果是多个查询会产生较多的重复代码,这时候就可以使用模板机制,通过观察我们发现上面步骤中大多数都是重复的,可复用的,只有在遍历ResultSet并封装成集合的这一步骤是可定制的,因为每张表都映射不同的java bean。这部分代码是没有办法复用的,只能定制。

抽象类代码:

 public abstract class JdbcTemplate {  
  
    //模板方法
    public final Object execute(String sql) throws SQLException{
     
        Connection con = HsqldbUtil.getConnection();  
        Statement stmt = null;  
        try {  
   
            stmt = con.createStatement();  
            ResultSet rs = stmt.executeQuery(sql);  
            Object result = doInStatement(rs);//抽象方法(定制方法,需要子类实现)   
            return result;  
        }  
        catch (SQLException ex) {  
             ex.printStackTrace();  
             throw ex;  
        }  
        finally {  
   
            try {  
                stmt.close();  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
            try {  
                if(!con.isClosed()){  
                    try {  
                        con.close();  
                    } catch (SQLException e) {  
                        e.printStackTrace();  
                    }  
                }  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
              
        }  
    }  
      
    //抽象方法(定制方法)
    protected abstract Object doInStatement(ResultSet rs);  
}  

这个抽象类中,封装了SUN JDBC API的主要流程,而遍历ResultSet这一步骤则放到抽象方法doInStatement()中,由子类负责实现。

子类实现代码:

 public class JdbcTemplateUserImpl extends JdbcTemplate {  
  
    @Override  
    protected Object doInStatement(ResultSet rs) {  
        List<User> userList = new ArrayList<User>();  
          
        try {  
            User user = null;  
            while (rs.next()) {  
  
                user = new User();  
                user.setId(rs.getInt("id"));  
                user.setUserName(rs.getString("user_name"));  
                user.setBirth(rs.getDate("birth"));  
                user.setCreateDate(rs.getDate("create_date"));  
                userList.add(user);  
            }  
            return userList;  
        } catch (SQLException e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
  
}  

我们在doInStatement()方法中,对ResultSet进行了遍历,最后并返回。

测试代码:

 String sql = "select * from User";  
JdbcTemplate jt = new JdbcTemplateUserImpl();  
List<User> userList = (List<User>) jt.execute(sql);  

模板机制的使用到此为止,但是如果每次调用jdbcTemplate时,都要继承一下上面的父类,这样挺不方便的,这样回调机制就可以发挥作用了。

所谓回调,就是方法参数中传递一个接口,父类在调用此方法时,必须调用方法中传递的接口的实现类。

回调加模板模式实现

回调接口:

 public interface StatementCallback {  
    Object doInStatement(Statement stmt) throws SQLException;  
}  

模板方法:

 public class JdbcTemplate {  

     //模板方法
public final Object execute(StatementCallback action) throws SQLException{ Connection con = HsqldbUtil.getConnection();
Statement stmt = null;
try { stmt = con.createStatement();
Object result = action.doInStatement(rs);//回调方法
return result;
}
catch (SQLException ex) {
ex.printStackTrace();
throw ex;
}
finally { try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(!con.isClosed()){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
} }
}
}
public Object query(StatementCallback stmt) throws SQLException{
return execute(stmt);
}
}

测试的类:

 public Object query(final String sql) throws SQLException {  
        class QueryStatementCallback implements StatementCallback {  
  
            public Object doInStatement(Statement stmt) throws SQLException {  
                ResultSet rs = stmt.executeQuery(sql);  
                List<User> userList = new ArrayList<User>();  
  
                User user = null;  
                while (rs.next()) {  
  
                    user = new User();  
                    user.setId(rs.getInt("id"));  
                    user.setUserName(rs.getString("user_name"));  
                    user.setBirth(rs.getDate("birth"));  
                    user.setCreateDate(rs.getDate("create_date"));  
                    userList.add(user);  
                }  
                return userList;  
  
            }  
  
        }  
  
        JdbcTemplate jt = new JdbcTemplate();  
        return jt.query(new QueryStatementCallback());  
    }  

为什么spring不用传统的模板方法,而加之以Callback进行配合呢? 
试想,如果父类中有10个抽象方法,而继承它的所有子类则要将这10个抽象方法全部实现,子类显得非常臃肿。而有时候某个子类只需要定制父类中的某一个方法该怎么办呢?这个时候就要用到Callback回调了。 

另外,上面这种方式基本上实现了模板方法+回调模式。但离spring的jdbcTemplate还有些距离。 我们上面虽然实现了模板方法+回调模式,但相对于Spring的JdbcTemplate则显得有些“丑陋”。Spring引入了RowMapper和ResultSetExtractor的概念。 RowMapper接口负责处理某一行的数据,例如,我们可以在mapRow方法里对某一行记录进行操作,或封装成entity。 ResultSetExtractor是数据集抽取器,负责遍历ResultSet并根据RowMapper里的规则对数据进行处理。 RowMapper和ResultSetExtractor区别是,RowMapper是处理某一行数据,返回一个实体对象。而ResultSetExtractor是处理一个数据集合,返回一个对象集合。

  当然,上面所述仅仅是Spring JdbcTemplte实现的基本原理,Spring JdbcTemplate内部还做了更多的事情,比如,把所有的基本操作都封装到JdbcOperations接口内,以及采用JdbcAccessor来管理DataSource和转换异常等。

java模板和回调机制学习总结的更多相关文章

  1. Java中的回调函数学习

    Java中的回调函数学习 博客分类: J2SE JavaJ#  一般来说分为以下几步: 声明回调函数的统一接口interface A,包含方法callback(); 在调用类caller内将该接口设置 ...

  2. 夯实Java基础系列11:深入理解Java中的回调机制

    目录 模块间的调用 多线程中的"回调" Java回调机制实战 实例一 : 同步调用 实例二:由浅入深 实例三:Tom做题 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 ...

  3. (翻译)理解Java当中的回调机制

    原文地址:http://cleancodedevelopment-qualityseal.blogspot.com/2012/10/understanding-callbacks-with-java. ...

  4. Java中的回调函数学习-深入浅出

    Java中的回调函数一般来说分为下面几步: 声明回调函数的统一接口interface A.包括方法callback(); 在调用类caller内将该接口设置为私有成员private A XXX; 在c ...

  5. Android开发学习之路-回调机制学习笔记

    不知道是我学Java的时候没有认真听还是怎么的,曾经一直不知道什么是“回调”,它有什么用,百度一大堆,都太复杂看不明白(好吧是我笨),所以想把自己理解的分享给其他看到的人,大家都真正认识一下这个重要的 ...

  6. JAVA事件监听机制学习

    //事件监听机制 import java.awt.*; import java.awt.event.*; public class TestEvent { public static void mai ...

  7. Java自动内存管理机制学习(二):垃圾回收器与内存分配策略

    备注:本文引自<深入理解Java虚拟机第二版>仅供参考 图片来自:http://csdn.net/WSYW126 垃圾收集器与内存分配策略 概述 GC要完成3件事: 哪些内存需要回收? 什 ...

  8. Java自动内存管理机制学习(一):Java内存区域与内存溢出异常

    备注:本文引用自<深入理解Java虚拟机第二版> 2.1 运行时数据区域 Java虚拟机在执行Java程序的过程中把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创 ...

  9. <<深入Java虚拟机>>-虚拟机类加载机制-学习笔记

    类加载的时机 遇到new.getstatic.putstatic或invokestatic这4个字节码指令时,如果类没有进行过初始化,则需要先触发其初始化.生成这4条指令最常见的Java场景是:使用n ...

随机推荐

  1. Linux shell basic3 dd wc comm chmod ls

    Generating files of any size /dev/zerois a character special device, which infinitely returns the ze ...

  2. NOIP2008普及组传球游戏(动态规划)——yhx

    题目描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏.这次,老师带着同学们一起做传球游戏. 游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同 ...

  3. ZOJ 1111 Poker Hands --复杂模拟

    昨天晚上写的,写了一个多小时,9000+B,居然1A了,爽. 题意:玩扑克,比大小.规则如下: 题意很简单,看过赌神的人都知道,每人手中5张排,比牌面大小,牌面由大到小分别是(这里花色无大小),级别从 ...

  4. Codeforces Round #FF/#255 D DZY Loves Modification --贪心+优先队列

    题意:给你一个矩阵,每次选某一行或者某一列,得到的价值为那一行或列的和,然后该行每个元素减去p.问连续取k次能得到的最大总价值为多少. 解法: 如果p=0,即永远不减数,那么最优肯定是取每行或每列那个 ...

  5. [C#] 委托之Action和Func区别

    一.说明 一般我们定义委托都是有如下两步: public delegate void MyDelegate(string name);//定义委托 public MyDelegate myDelega ...

  6. BUG描述规范

    BUG描述规范 一. 目的与适用范围 1.1 目的 报告软件测试错误的目的是为了保证修复错误的人员可以明确报告的错误,从而有利于分析错误产生的原因,定位错误,然后修正之.因此,报告软件测试错误的基本要 ...

  7. Eclipse去除JavaScript验证错误

    这篇文章主要是对Eclipse去除js(JavaScript)验证错误进行了介绍.在Eclipse中,js文件常常会报错.可以通过如下几个步骤解决 第一步: 去除eclipse的JS验证: 将wind ...

  8. 较多java书籍的网站 tools138.com

    http://www.tools138.com/front/resource/java_book.jsp

  9. 025医疗项目-模块二:药品目录的导入导出-HSSF导入类的封装

    上一篇文章提过,HSSF的用户模式会导致读取海量数据时很慢,所以我们采用的是事件驱动模式.这个模式类似于xml的sax解析.需要实现一个接口,HSSFListener接口. 原理:根据excel底层存 ...

  10. Linux 网络编程二(Socket创建)

    TCP通信 一个程序使用套接字需要执行4个步骤. --分配套接口和初始化 --连接 --发送或接收数据 --关闭套接字 涉及到的调用包括socket.bind.listen.connect(阻塞线程) ...