最近看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());  
     }  

个抽象方法,而继承它的所有子类则要将这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当中的回调机制

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

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

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

  4. JAVA事件监听机制学习

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

  5. &lt;&lt;深入Java虚拟机&gt;&gt;-虚拟机类加载机制-学习笔记

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

  6. JAVA回调机制(CallBack)详解

    序言 最近学习java,接触到了回调机制(CallBack).初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义.当然了,我在理解了回 ...

  7. JAVA 回调机制(callback)

    序言 最近学习java,接触到了回调机制(CallBack).初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义.当然了,我在理解了回 ...

  8. JVM学习001通过实例总结Java虚拟机的运行机制

    JVM学习(1)——通过实例总结Java虚拟机的运行机制-转载http://www.cnblogs.com/kubixuesheng/p/5199200.html 文章转载自:http://www.c ...

  9. JAVA回调机制解析

    一.回调机制概述     回调机制在JAVA代码中一直遇到,但之前不懂其原理,几乎都是绕着走.俗话说做不愿意做的事情叫做突破,故诞生了该文章,算是新年的新气象,新突破!     回调机制是什么?其实回 ...

随机推荐

  1. XSS 前端防火墙 —— 可疑模块拦截

    上一篇介绍的系统,已能预警现实中的大多数 XSS 攻击,但想绕过还是很容易的. 由于是在前端防护,策略配置都能在源代码里找到,因此很快就能试出破解方案.并且攻击者可以屏蔽日志接口,在自己电脑上永不发出 ...

  2. linux 下使用配置文件

    最近鼓捣双机热备,写了些shell脚本 适用配置文件 以 ifbeat 为例,linux的标准的配置文件中等号两遍不能存在括号, 空格 或特殊符号可以用转衣服 ' \' beat=true prima ...

  3. 【从零开始学习Hadoop】--2.HDFS分布式文件系统

    1. 文件系统从头说2. Hadoop的文件系统3. 如何将文件复制到HDFS3.1 目录和文件结构3.2 FileCopy.java文件的源代码3.3 编译3.4打包3.5 运行3.6 检查结果 1 ...

  4. 学习CSS的瓶颈

    何为学习瓶颈 学习到了一定的阶段,就很难继续提高水平的一种现象 这是很多人都正面对的,但同时自己并未意识到. 既然是瓶颈,那么一旦突破了,就是广阔天空! 你是否经常面对这样的情景: 遇到一个奇葩问题, ...

  5. 【POJ】1151 Atlantis(线段树)

    http://poj.org/problem?id=1151 经典矩形面积并吧.....很简单我就不说了... 有个很神的地方,我脑残没想到: 将线段变成点啊QAQ这样方便计算了啊 还有个很坑的地方, ...

  6. SSH整合JBPM4.4

    第一步:导入所需jar包: 所需的jar包(使用了hibernate annotation和struts2的convention-plugin,可能有多余的包,没做清理): 第二步:修改jbpm配置文 ...

  7. 20145235 《Java程序设计》实验二

    实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验步骤 单元测试 代码及则是结果: public cla ...

  8. [Excel]C#操作Excel(导入导出)

    /// <summary> /// 读取Excel文档 /// </summary> /// <param name="Path">文件名称&l ...

  9. hdu1213 How Many Tables

    How Many Tables Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  10. 【BZOJ】【2818】Gcd

    欧拉函数/莫比乌斯函数 嗯……跟2190很像的一道题,在上道题的基础上我们很容易就想到先求出gcd(x,y)==1的组,然后再让x*=prime[i],y*=prime[i]这样它们的最大公约数就是p ...