最近看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、測試回呼函數
reee的使用過程中。模板方法設計模式就使用方法回呼的機制,該模式首先定義特定的步驟的演算法骨架,而將一些步驟延遲到子類別中去實現的設計模式。模板方法設計模式使得子類別可以不改變一個演算法的結構即可重新定義該演算法的某些特定步驟。
模板方式設計模式的適用性:
1、一次實作一個演算法的不變部分,將可變的演算法留給子類別來實現。
2、各子類別中公共的行為應該被提取出來並集中一個公共父類別中以避免程式碼重複。
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(); } }
子類別實作範本方式類別:
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("子类的实现方法"); } }JdbcTemplete為例,詳細說明模板模式和回呼機制的使用。
首先看一下經典的JDBC程式設計的例子:
public class TempleteTest { public static void main(String[] args) { SubClass subClass = new SubClass(); subClass.print(); subClass.doPrint(); } }
一個簡單的查詢,就要做這麼一大堆事情,而且還要處理異常,我們不防來梳理一下:
1、獲取connection2、獲取statement
3、取得resultset
5、依序關閉connection,statement,resultset,而且還要考慮各種例外等等。
如果是多個查詢會產生較多的重複程式碼,這時候就可以使用模板機制,透過觀察我們發現上面步驟中大多數都是重複的,可複用的,只有在遍歷ResultSet並封裝成集合的這一步驟是可自訂的,因為每張表都映射不同的java bean。這部分程式碼是沒有辦法重複使用的,只能客製化。
抽象類別程式碼:
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; }這個抽象類別中,封裝了SUN JDBC API的主要流程,而遍歷ResultSet這一步驟則放到抽象方法doInStatement()中,由子類別負責實作。 子類別實作程式碼:
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); }我們在doInStatement()方法中,對ResultSet進行了遍歷,最後並回傳。 測試程式碼:
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; } } }模板機制的使用到此為止,但是如果每次調用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模板和回调机制相关文章请关注PHP中文网!