最近看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、回调接口

1 public interface Callback {
2     String callBack();
3 }

2、调用者

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

3、测试回调函数

 1 public class TestCallcack {
 2     public static void main(String[] args) {
 3         //创建调用者的实现类
 4         Another another = new Another();
 5         //将回掉接口注册到实现类中
 6         another.setCallback(new Callback() {
 7             @Override
 8             public String callBack() {
 9                 return "you are a pig";
10             }
11         });
12         //执行回调函数
13         another.doCallback();
14     }
15 }

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

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

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

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

  3、可以控制子类扩展。

模板实例:

抽象模板方法类:

 1 public abstract class AbstractSup {
 2         //需要子类实现的方法
 3     public abstract void print();
 4         //模板方法
 5     public void doPrint(){
 6         System.out.println("执行模板方法");
 7         for (int i = 0; i < 3; i++) {
 8             print();
 9         }
10     }
11 }

子类实现模板方式类:

1 public class SubClass extends AbstractSup{
2     @Override
3     public void print() {
4         System.out.println("子类的实现方法");
5     }
6
7 }

模板方法测试类:

1 public class TempleteTest {
2     public static void main(String[] args) {
3         SubClass subClass = new SubClass();
4         subClass.print();
5         subClass.doPrint();
6     }
7 }

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

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

 1 public List<User> query() {
 2
 3     List<User> userList = new ArrayList<User>();
 4     String sql = "select * from User";
 5
 6     Connection con = null;
 7     PreparedStatement pst = null;
 8     ResultSet rs = null;
 9     try {
10         con = HsqldbUtil.getConnection();
11        pst = con.prepareStatement(sql);
12         rs = pst.executeQuery();
13
14         User user = null;
15         while (rs.next()) {
16
17             user = new User();
18             user.setId(rs.getInt("id"));
19             user.setUserName(rs.getString("user_name"));
20             user.setBirth(rs.getDate("birth"));
21             user.setCreateDate(rs.getDate("create_date"));
22             userList.add(user);
23         }
24
25
26     } catch (SQLException e) {
27         e.printStackTrace();
28     }finally{
29         if(rs != null){
30             try {
31                 rs.close();
32             } catch (SQLException e) {
33                 e.printStackTrace();
34             }
35         }
36         try {
37             pst.close();
38         } catch (SQLException e) {
39             e.printStackTrace();
40         }
41         try {
42            if(!con.isClosed()){
43                 try {
44                     con.close();
45               } catch (SQLException e) {
46                     e.printStackTrace();
47                 }
48             }
49         } catch (SQLException e) {
50             e.printStackTrace();
51         }
52
53     }
54     return userList;
55 }  

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

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

抽象类代码:

 1 public abstract class JdbcTemplate {
 2
 3     //模板方法
 4     public final Object execute(String sql) throws SQLException{
 5
 6         Connection con = HsqldbUtil.getConnection();
 7         Statement stmt = null;
 8         try {
 9
10             stmt = con.createStatement();
11             ResultSet rs = stmt.executeQuery(sql);
12             Object result = doInStatement(rs);//抽象方法(定制方法,需要子类实现)
13             return result;
14         }
15         catch (SQLException ex) {
16              ex.printStackTrace();
17              throw ex;
18         }
19         finally {
20
21             try {
22                 stmt.close();
23             } catch (SQLException e) {
24                 e.printStackTrace();
25             }
26             try {
27                 if(!con.isClosed()){
28                     try {
29                         con.close();
30                     } catch (SQLException e) {
31                         e.printStackTrace();
32                     }
33                 }
34             } catch (SQLException e) {
35                 e.printStackTrace();
36             }
37
38         }
39     }
40
41     //抽象方法(定制方法)
42     protected abstract Object doInStatement(ResultSet rs);
43 }  

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

子类实现代码:

 1 public class JdbcTemplateUserImpl extends JdbcTemplate {
 2
 3     @Override
 4     protected Object doInStatement(ResultSet rs) {
 5         List<User> userList = new ArrayList<User>();
 6
 7         try {
 8             User user = null;
 9             while (rs.next()) {
10
11                 user = new User();
12                 user.setId(rs.getInt("id"));
13                 user.setUserName(rs.getString("user_name"));
14                 user.setBirth(rs.getDate("birth"));
15                 user.setCreateDate(rs.getDate("create_date"));
16                 userList.add(user);
17             }
18             return userList;
19         } catch (SQLException e) {
20             e.printStackTrace();
21             return null;
22         }
23     }
24
25 }  

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

测试代码:

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

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

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

回调加模板模式实现

回调接口:

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

模板方法:

 1 public class JdbcTemplate {
 2
 3     //模板方法
 4     public final Object execute(StatementCallback action) throws SQLException{
 5
 6         Connection con = HsqldbUtil.getConnection();
 7         Statement stmt = null;
 8         try {
 9
10             stmt = con.createStatement();
11             Object result = action.doInStatement(rs);//回调方法
12             return result;
13         }
14         catch (SQLException ex) {
15              ex.printStackTrace();
16              throw ex;
17         }
18         finally {
19
20             try {
21                 stmt.close();
22             } catch (SQLException e) {
23                 e.printStackTrace();
24             }
25             try {
26                 if(!con.isClosed()){
27                     try {
28                         con.close();
29                     } catch (SQLException e) {
30                         e.printStackTrace();
31                     }
32                 }
33             } catch (SQLException e) {
34                 e.printStackTrace();
35             }
36
37         }
38     }
39     }
40     public Object query(StatementCallback stmt) throws SQLException{
41         return execute(stmt);
42     }
43 } 

测试的类:

 1 public Object query(final String sql) throws SQLException {
 2         class QueryStatementCallback implements StatementCallback {
 3
 4             public Object doInStatement(Statement stmt) throws SQLException {
 5                 ResultSet rs = stmt.executeQuery(sql);
 6                 List<User> userList = new ArrayList<User>();
 7
 8                 User user = null;
 9                 while (rs.next()) {
10
11                     user = new User();
12                     user.setId(rs.getInt("id"));
13                     user.setUserName(rs.getString("user_name"));
14                     user.setBirth(rs.getDate("birth"));
15                     user.setCreateDate(rs.getDate("create_date"));
16                     userList.add(user);
17                 }
18                 return userList;
19
20             }
21
22         }
23
24         JdbcTemplate jt = new JdbcTemplate();
25         return jt.query(new QueryStatementCallback());
26     }  

为什么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的回调机制--学习笔记

    本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273) 所谓回调: 就是A类中调用B类中的某个方法C,然后B ...

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

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  3. 面试官:你知道Java中的回调机制吗?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:22j.co/cFPf          正文    调用和 ...

  4. 面试:你知道Java中的回调机制吗?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者 | 带妳心菲 来源 | cnblogs.com/prayjo ...

  5. 面试:你知道 Java 中的回调机制吗?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者 | 带妳心菲 来源 | cnblogs.com/prayjo ...

  6. c++ 异步回调_知道Java中的回调机制吗?

    调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b( ...

  7. Java中的回调机制,这篇给你整的明明白白的

    作者:带妳心菲 cnblogs.com/prayjourney/p/9667835.html 调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: ...

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

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

  9. java 重点!反射机制学习

    反射机制:java语言通过此可以更改字节码文件(class文件),类似黑客 与之相关的类有java.class java.lang.reflect.Field,类的属性,静态变量,实例变量 java. ...

  10. Java回调机制是什么意思?

    华清远见成都中心 2017-09-20 10:39 一.Java回调机制是什么意思 Java中的回调机制是什么意思呢?所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D ...

最新文章

  1. 在使用Reference Source调试.Net 源代码时如何取消optimizations(代码优化)-翻译
  2. 从创作工具到虚拟超现实主义,聊一聊VR的艺术王国
  3. MySQL主从库--同步异常
  4. matlab水印剪切攻击程序,可以运行的水印matlab程序(嵌入,提取,攻击测试等).doc
  5. Android广播的学习和使用
  6. 转.h和.cpp文件的区别
  7. 使用.NET5、Blazor和Electron.NET构建跨平台桌面应用
  8. [vue] vue项目有使用过npm run build --report吗?
  9. wordpress主题的样式修改
  10. go tcp socket
  11. 图解 MySQL 索引,写得实在太好了!
  12. pandas获取索引行数据
  13. Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(
  14. Amber分子动力学模拟
  15. WordPress 跨站请求伪造漏洞
  16. C语言计算内部回报率(或名内部收益率)(IRR)
  17. Java深入理解深拷贝和浅拷贝区别
  18. 文件搜索工具ProFind for Mac
  19. niushop多商户商户端手机uniapp源码v4单商户v4_Saas开源版含uniapp以及niushop社区团购标准版源码开源的区别
  20. 从事游戏开发需要什么技能

热门文章

  1. 知识表示与计算机,两分钟了解人工智能中的“知识与知识表示”
  2. fatal: empty string is not a valid pathspec, please use . instead if you meant to match all paths
  3. PHP分批次处理数据
  4. plesk 打不开php,如何在 Plesk 中管理 PHP
  5. 给JDK设置tmp目录的办法
  6. /usr/bin/ld: reader.o: Relocations in generic ELF (EM: 62)
  7. CentOS安装NVidia驱动提示kernel source path问题
  8. error LNK2019: 无法解析的外部符号 _WinMain,在函数_tmainCRTStartup中被引用
  9. 毛氏生产:经常集体文化娱乐活动,有何利弊
  10. 遇到问题,尽量自己解决,实在解决不了再问别人