最近看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. Java自动内存管理机制学习(二):垃圾回收器与内存分配策略

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

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

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

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

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

  8. Java基础9:解读Java回调机制

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

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

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

随机推荐

  1. 配置ogg异构oracle-mysql(2)源端配置

    源端配置大致分为如下三个步骤:配置mgr,配置抽取进程,配置投递进程 在源端先创建一张表,记得带主键: SQL> create table ah4(id int ,name varchar(10 ...

  2. Extjs读取本地下拉选框数据源,分为text和value,显示text,传值value

    this.rdTypeCom=new Ext.form.ComboBox({              hiddenName:'rdType',              store:new Ext. ...

  3. linux工具之log4j-LogBack-slf4j-commons-logging

    log4j http://commons.apache.org/proper/commons-logging/ http://logging.apache.org/log4j/2.x/ The Com ...

  4. (easy)LeetCode 205.Isomorphic Strings (*)

    Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the chara ...

  5. java数据结构之列表——ArrayList,LinkedList,比较

    刚看完<数据结构与算法分析java语言描述>的第3章中的表,下面回忆下主要知识点,主要说明各列表之间的关系,以及各自的优缺点.其中牵涉到内部类和嵌套类. 1 Collection APIp ...

  6. http 错误编号大全(转)

      状态行包含HTTP版本.状态代码.与状态代码对应的简短说明信息.在大多数情况下,除了Content-Type之外的所有应答头都是可选的.但Content-Type是必需的,它描述的是后面文档的MI ...

  7. Vs2010 配置驱动的开发环境

    我已被用来VS2010开发环境,之前曾经与vs2010驱动的开发环境.重装系统,一次又一次的配置,找了好几篇文章,配置没有成功,在配置阶段突然成功了,直接把原来的驱动程序的配置文件将能够接管使用. 当 ...

  8. 201521123102 《Java程序设计》第5周学习总结

    1. 本周学习总结 2. 书面作业 Q1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误.并分析输出结果. 不能 ...

  9. maven的安装配置以及在IDEA中配置

    一.下载maven: 1.maven官网:http://maven.apache.org/download.cgi 二.安装配置 1.下载后解压到本地目录,如图 2.配置环境变量:我的电脑右键-> ...

  10. 灵雀云CTO陈恺:从“鸿沟理论”看云原生,哪些技术能够跨越鸿沟?

    灵雀云CTO陈恺:从“鸿沟理论”看云原生,哪些技术能够跨越鸿沟? 历史进入2019年,放眼望去,今天的整个技术大环境和生态都发生了很大的变化.在己亥猪年春节刚刚过去的早春时节,我们来梳理和展望一下整个 ...