Javaテンプレートとコールバック機構の詳しい説明

高洛峰
リリース: 2017-01-24 13:37:24
オリジナル
1219 人が閲覧しました

最近、Spring の JDBCTemplete のテンプレート メソッド呼び出しを見て、テンプレートとコールバックに非常に興味を持ち、いくつかの情報を調べてまとめました。

コールバック関数:

いわゆるコールバックとは、クライアントプログラム C がサービスプログラム S 内の特定の関数 A を呼び出し、次に S が C 内の特定の関数 B を特定のタイミングで呼び出すことを意味します。 B コールバック関数と呼ばれます。コールバック関数は単なる機能の断片であり、コールバック関数呼び出し規則に従ってユーザーによって実装される関数です。コールバック関数はワークフローの一部であり、ワークフローによって関数呼び出し (コールバック) のタイミングが決定されます。一般に、C が B を自ら呼び出すことはありません。C が B を提供する目的は、S に B を呼び出すことであり、C はそれを提供する必要があります。 S は C が提供する B の名前を知らないため、S は B のインターフェース仕様 (関数プロトタイプ) を合意し、C は事前に S の関数 R を通じて B の関数を使用することを S に伝えます。関数の登録をコールバック、R を登録関数と呼びます。 Web サービスと Java の RMI はどちらもコールバック メカニズムを使用してリモート サーバー プログラムにアクセスします。コールバック関数には次の特徴があります:

1. ワークフローの一部である; 2. ワークフローで指定された呼び出し規則に従って宣言 (定義) される必要がある; 3. 呼び出しのタイミングはワークフローによって決定される。コールバック関数の実装者は、ワークフロー関数を実装するためにコールバック関数を直接呼び出すことはできません。

コールバック メカニズム: コールバック メカニズムは、合意されたインターフェイスに従ってワークフロー内の特定の関数をワークフローに公開します。外部ユーザーは、外部ユーザーにデータを提供するか、外部ユーザーにデータの提供を要求します。

Java コールバック メカニズム:

ソフトウェア モジュール間には、メソッドの呼び出しに関して、常に特定のインターフェイスが存在し、同期呼び出し、コールバック、非同期呼び出しの 3 つのカテゴリに分類できます。

同期呼び出し: 呼び出し元は、相手が実行を完了するまで待機してから戻る必要があります。

リターン呼び出し: 双方向呼び出しモード、つまり呼び出し先。呼び出し時には、相手のインターフェースも呼び出されます。

非同期呼び出し: メッセージやイベントと同様のメカニズムですが、インターフェースのサービスが特定のメッセージを受信したときの呼び出しの方向はまったく逆です。またはイベントが発生すると、クライアント側に積極的に通知します (つまり、クライアントのインターフェイスを呼び出します)。

コールバックと非同期呼び出しの関係は非常に密接です。コールバックは非同期メッセージの登録に使用され、非同期呼び出しはメッセージの通知に使用されます。

コールバックインスタンス

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());
  }
}
ログイン後にコピー

通常、コールバックメソッドは「Javaインターフェース」と「抽象クラス」で使用されます。使用中。テンプレート メソッド設計パターンでは、メソッド コールバック メカニズムを使用します。このパターンでは、最初に特定のステップのアルゴリズム スケルトンを定義し、一部のステップをサブクラスに延期して実装します。テンプレート メソッド設計パターンを使用すると、アルゴリズムの構造を変更せずに、サブクラスでアルゴリズムの特定のステップを再定義できます。

テンプレートデザインパターンの適用性:

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("子类的实现方法");
  }
 
}
ログイン後にコピー

以下は、 Spring テンプレートのメソッド JdbcTemplete を例として、テンプレート モードとコールバック メカニズムの使用法を詳しく説明します。

まず、古典的な JDBC プログラミングの例を見てみましょう:

public class TempleteTest {
  public static void main(String[] args) {
    SubClass subClass = new SubClass();
    subClass.print();
    subClass.doPrint();
  }
}
ログイン後にコピー

単純なクエリは多くのことを実行する必要があり、例外も処理する必要があります:

1. Get 接続。ステートメント

3. 結果セットを取得します

4. 結果セットをトラバースしてコレクションにカプセル化します

5. 接続、ステートメント、結果セットを順番に閉じ、さまざまな例外などを考慮します。

複数のクエリによってさらに重複するコードが生成される場合は、この時点でテンプレート メカニズムを使用できることが観察によりわかりました。このステップは、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中文网!

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!