Spring JdbcTemplate源码阅读报告
- 写在前面
spring一直以删繁就简为主旨,所以设计出非常流行的bean管理模式,简化了开发中的Bean的管理,少写了很多重复代码。而JdbcTemplate的设计更令人赞叹,轻量级,可做ORM也可如Jdbc般灵活。而在JdbcTemplate一个类中,蕴含了两种设计模式,阅读之后受益匪浅,今日特此总结。
- 设计模式基础
如果硬读,则走火入魔只得皮毛,幸得某篇博客的指点,先对设计模式进行熟悉,了解它的构造,再去阅读,不仅事半功倍还能加强理解。
jdbcTemplate中蕴含的两个设计模式:流程控制模式、回调模式。
所谓流程控制,就是由父类定义一个方法,方法中蕴含多个流程,把固定的流程私有化,在其中调用,则子类无法对私有化方法进行重写;若将流程进行默认实现,则子类可选择实现;若将流程抽象化,则推迟到子类去实现,也就是定制化实现,呈上代码。
譬如:煮牛肉,必须先切,腌(必要过程) 热锅放油(默认实现) 下锅炒(定制过程)
public abstract class Father{ //流程 m1->m2->m3 public final void templateMethod(StatementCallback statementCallback){ method1(); method2(); //抽象方法 子类负责实现 通常为业务 statementCallback.method3();//由接口提供 } //父类私有方法 不能重写 流程控制 private void method1(){ System.out.println("father private method, control by father "); } //钩子方法 可实现 父类提供默认实现 public void method2(){ System.out.println("default method,prevent by father"); } }
如上类,清晰的流程模型,基于method2定制化实现。这个设计模式有什么好处呢?
在我们刚学习jdbc的时候,最常见的代码,莫过于。
String driver="com.mysql.jdbc.Driver"; Connection con; String url="jdbc:mysql://localhost:3306/demo"; String user="root"; String pwd="12345"; //连接上数据库mysql public void connection2MYSQL() { try { Class.forName(driver); con=DriverManager.getConnection(url,user,pwd); if(!con.isClosed()) System.out.println("连接成功"); } catch (Exception e) { e.printStackTrace(); } }
在反反复复的写流程之后,设计者意识到这种无用功减慢效率,必须使用一种设计模式来让程序员注意力更多得关注到更有价值的事情之上,所以这种固定流程,就纳入了流程中父类定义,并且不能修改的范围内。
第二种设计模式,回调模式。
在现代互联网随处可见回调,这就是多态性的好处之一,只要需要,就可以定制,诸如
$("#button").click(function(){ //logic });
这种回调模式,在程序设计中大受欢迎,如果加上异步,则就是异步回调模型。它受欢迎的原因,就是定制化。而Java中,构建回调模式只需要定义如下接口,并结合上述流程模式。
public interface StatementCallback { public void method3(); }
所谓的定制,就是:等我需要的时候再叫你。
public class Son extends Father{ public void method3(){ System.out.println("implement by son"); } } public class Main { public static void main(String[] args){ Father patten = new Son(); patten.templateMethod(new StatementCallback() { @Override public void method3() { System.out.println("implement by user,callback"); } }); } }
- 源码剖析
它的各个方法最后都转发到此方法来,此方法整体就是申请连接,执行sql,拿到结果,最后释放连接,在红色标注的地方就是定制化的体现。它封装了很多个上层方法,比如queryForList,queryForMap,都是返回它封装的数据结构。而最底层的流程就是上述方法。
它提供默认实现的方式是匿名内部类。
excute是为了执行sql而设计的,而里面的query方法和update则就是为了读写分别设计的了。
用过的人都知道,update返回int表示它操作的affects数目,而jdbc流行的一种返回主键的做法,就是在update中实现一个返回主键的逻辑,它用到的这个接口就是。
下面代码重写了回调接口,使其返回插入的主键。
int col = 0; KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(new PreparedStatementCreator() { public PreparedStatement createPreparedStatement(Connection con) throws SQLException { PreparedStatement ps = con.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS); for(int i=0;i<args.length;i++){ ps.setObject(i+1,args[i]); } return ps; } }, keyHolder); col = keyHolder.getKey().intValue();
其余接口未细读,而对于jdbc来说,无非就是读写,我们平常使用的是List和Map,它也可以封装Object,充当小型ORM,它以优秀的设计模式使得数据库操作层的业务设计变得非常简单清晰,并且它优秀的泛型处理技术,也是令人叹为观止。