首頁 Java java教程 詳解java模板和回呼機制

詳解java模板和回呼機制

Jan 24, 2017 pm 01:37 PM

最近看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、獲取connection 

2、獲取statement 
3、取得resultset 

4、遍歷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中文网!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1653
14
CakePHP 教程
1413
52
Laravel 教程
1306
25
PHP教程
1251
29
C# 教程
1224
24
公司安全軟件導致應用無法運行?如何排查和解決? 公司安全軟件導致應用無法運行?如何排查和解決? Apr 19, 2025 pm 04:51 PM

公司安全軟件導致部分應用無法正常運行的排查與解決方法許多公司為了保障內部網絡安全,會部署安全軟件。 ...

如何將姓名轉換為數字以實現排序並保持群組中的一致性? 如何將姓名轉換為數字以實現排序並保持群組中的一致性? Apr 19, 2025 pm 11:30 PM

將姓名轉換為數字以實現排序的解決方案在許多應用場景中,用戶可能需要在群組中進行排序,尤其是在一個用...

如何優雅地獲取實體類變量名構建數據庫查詢條件? 如何優雅地獲取實體類變量名構建數據庫查詢條件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架進行數據庫操作時,經常需要根據實體類的屬性名構造查詢條件。如果每次都手動...

如何使用MapStruct簡化系統對接中的字段映射問題? 如何使用MapStruct簡化系統對接中的字段映射問題? Apr 19, 2025 pm 06:21 PM

系統對接中的字段映射處理在進行系統對接時,常常會遇到一個棘手的問題:如何將A系統的接口字段有效地映�...

Java對像如何安全地轉換為數組? Java對像如何安全地轉換為數組? Apr 19, 2025 pm 11:33 PM

Java對象與數組的轉換:深入探討強制類型轉換的風險與正確方法很多Java初學者會遇到將一個對象轉換成數組的�...

IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本啟動Spring...

電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? 電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? Apr 19, 2025 pm 11:27 PM

電商平台SKU和SPU表設計詳解本文將探討電商平台中SKU和SPU的數據庫設計問題,特別是如何處理用戶自定義銷售屬...

如何利用Redis緩存方案高效實現產品排行榜列表的需求? 如何利用Redis緩存方案高效實現產品排行榜列表的需求? Apr 19, 2025 pm 11:36 PM

Redis緩存方案如何實現產品排行榜列表的需求?在開發過程中,我們常常需要處理排行榜的需求,例如展示一個�...

See all articles