关于 回调 的一些理解(以jdbctemplate为例)

来源:互联网 发布:武工队后勤部淘宝 编辑:程序博客网 时间:2024/05/17 07:57

以前一起没有很好的理解 “回调”,偶然的机会下,看了一下spring的jdbctemplate,觉得对回调的理解很有帮助,就稍微自己总结一下,以便日后温习.
回调: 就是在方法的参数中传递一个接口,父在调用此方法时,必须调用方法中传递的接口的实现类。
看一段代码:
定义一个接口,作为方法中的参数:

import java.sql.SQLException;  import java.sql.Statement;  public interface StatementCallback {        //这个doInStatement相当于上面的doResultSet      Object doInStatement(Statement stmt) throws SQLException;    }

定义一个方法,它的参数中含有上面定义的接口:

import java.sql.Connection;  import java.sql.DriverManager;  import java.sql.ResultSet;  import java.sql.SQLException;  import java.sql.Statement;  public  class JDBCTemplate  {      public Object execute(StatementCallback action) {          String  url="";          String  userName="";          String  password="";          Connection con=null;          Statement st=null;          ResultSet rs=null;          try{              Class.forName("com.mysql.jdbc.driver");            con=DriverManager.getConnection(url,userName,password);              st=con.createStatement();              Object object=action.doInStatement(st);              return object;                    }          //省略try catch          return null;      }      public  Object query(StatementCallback action){          return execute(action);      }  } 

下面的测试方法,只要给定一个sql,就能传回List

    @SuppressWarnings("unchecked")      public List<Student> test2(final String sql) {          JDBCTemplate jdbcTemplate = new JDBCTemplate();          return (List<Student>) jdbcTemplate.query(new StatementCallback() {              @Override              public Object doInStatement(Statement stmt) throws SQLException {                  ResultSet rs = stmt.executeQuery(sql);                  List<Student> userList = new ArrayList<Student>();                  Student user = null;                  while (rs.next()) {                      user = new Student();                      user.setId(rs.getInt("id"));                      user.setBirth(rs.getDate("birth"));                      userList.add(user);                  }                  return userList;              }          });      }  

在test中调用jdbctemplate的query()方法,参数为接口StatementCallback的一个实现类,并自定义实现了doInStatement()方法,当代码执行到Object object=action.doInStatement(st);时,会执行自定义实现了doInStatement()方法,实现回调.

再看一个难一点的示例,加深理解.
代码片段一:

    //主调用方法. 调用spring jdbctemplate的query()方法,参数中实现了RowCallbackHandler接口的processRow(ResultSet rs)方法.    jt.query(sql, params, new RowCallbackHandler() {              @Override              public void processRow(ResultSet rs) throws SQLException {                  Map<String, String> u = new HashMap<String, String>();                  u.put("id", rs.getString("id"));                  u.put("description", rs.getString("description"));                  list.add(u);            }          });  

以下均为spring jdbctemplate 源代码

代码片段二: 记为query_1()方法

    public void query(String sql, Object[] args, RowCallbackHandler rch) throws DataAccessException {          query(sql, newArgPreparedStatementSetter(args), rch);      }      protected PreparedStatementSetter newArgPreparedStatementSetter(Object[] args) {        return new ArgumentPreparedStatementSetter(args);    }public class ArgumentPreparedStatementSetter implements PreparedStatementSetter, ParameterDisposer {    private final Object[] args;    public ArgumentPreparedStatementSetter(Object[] args) {        this.args = args;    }    public void setValues(PreparedStatement ps) throws SQLException {        if(this.args != null) {            for(int i = 0; i < this.args.length; ++i) {                Object arg = this.args[i];                this.doSetValue(ps, i + 1, arg);            }        }    }    protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {        if(argValue instanceof SqlParameterValue) {            SqlParameterValue paramValue = (SqlParameterValue)argValue;            StatementCreatorUtils.setParameterValue(ps, parameterPosition, paramValue, paramValue.getValue());        } else {            StatementCreatorUtils.setParameterValue(ps, parameterPosition, -2147483648, argValue);        }    }}

代码片段三:记为query_2()方法

    public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException {        this.query((String)sql, (PreparedStatementSetter)pss, (ResultSetExtractor)(new JdbcTemplate.RowCallbackHandlerResultSetExtractor(rch)));    }    private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor<Object> {        private final RowCallbackHandler rch;        public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {            this.rch = rch;        }        public Object extractData(ResultSet rs) throws SQLException {            while(rs.next()) {                this.rch.processRow(rs);            }            return null;        }    }

代码片段四:记为query_3()方法

    public <T> T query(String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {        return this.query((PreparedStatementCreator)(new JdbcTemplate.SimplePreparedStatementCreator(sql)), (PreparedStatementSetter)pss, (ResultSetExtractor)rse);    }    private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {        private final String sql;        public SimplePreparedStatementCreator(String sql) {            Assert.notNull(sql, "SQL must not be null");            this.sql = sql;        }        public PreparedStatement createPreparedStatement(Connection con) throws SQLException {            return con.prepareStatement(this.sql);        }        public String getSql() {            return this.sql;        }    }

代码片段五:记为query_4()方法

    public <T> T query(                  PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)                  throws DataAccessException {              //注意匿名类              //execute一共两个参数 一个是PreparedStatementCreator 一个是PreparedStatementCallback              //***************3              return execute(psc, new PreparedStatementCallback<T>() {                  public T doInPreparedStatement(PreparedStatement ps) throws SQLException {                      ResultSet rs = null;                      try {                          if (pss != null) {                              pss.setValues(ps);                          }                          rs = ps.executeQuery();  //最最核心的一步 调用jdk的接口                          ResultSet rsToUse = rs;                          if (nativeJdbcExtractor != null) {                              rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);                          }                          return rse.extractData(rsToUse);                      }                      finally {                          JdbcUtils.closeResultSet(rs);                          if (pss instanceof ParameterDisposer) {                              ((ParameterDisposer) pss).cleanupParameters();                          }                      }                  }              });          }  

代码片段六:

    public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)                  throws DataAccessException {              Connection con = DataSourceUtils.getConnection(getDataSource());              PreparedStatement ps = null;              try {                  Connection conToUse = con;                  //*******1   获得PreparedStatement                  ps = psc.createPreparedStatement(conToUse);                  //这里是给PreparedStatement设置一些参数 基本不怎么用 不用深究                  applyStatementSettings(ps);                  PreparedStatement psToUse = ps;                  //跟上面的conToUse一样 暂且不管                  if (this.nativeJdbcExtractor != null) {                      psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);                  }                  //************2 回调方法 我们得回到代码3处                  T result = action.doInPreparedStatement(psToUse);                  handleWarnings(ps);                  return result;              }              catch (SQLException ex) {                  //              }              finally {              //              }          }

1.在代码片段一中,调用了spring jdbctemplate源码中的query_1()方法,并传递了自定义RowCallbackHandler接口实现类为参数.
2.在代码片段二中query_1()方法执行newArgPreparedStatementSetter(args)时,最终会调用ArgumentPreparedStatementSetter类的构造方法,将sql语句中的参数转变成ArgumentPreparedStatementSetter的属性,并将生成的ArgumentPreparedStatementSetter对象作为参数传给query_2().
3.在代码片段三中query_2()方法执行(ResultSetExtractor)(new JdbcTemplate.RowCallbackHandlerResultSetExtractor(rch))时,会将自定义实现的RowCallbackHandler接口实现类转变成ResultSetExtractor对象,并作为参数传递给query_3().
4.在代码片段四中query_3()方法执行(PreparedStatementCreator)(new JdbcTemplate.SimplePreparedStatementCreator(sql))时,将sql转变成PreparedStatementCreator对象,并传递给query_4().
5.在代码片段五中query_4()方法调用片段六中的execute()方法时,自定义实现了PreparedStatementCallback接口,并在实现PreparedStatementCallback接口时,使用了传入的参数PreparedStatementSetter对象和ResultSetExtractor对象,
6.在代码片段六中execute()方法执行T result = action.doInPreparedStatement(psToUse);时,回调方法,代码执行回到了在代码片段五中自定义实现PreparedStatementCallback接口的doInPreparedStatement()方法;
当doInPreparedStatement()中的代码执行到pss.setValues(ps)时,会调用代码片段二中生成的ArgumentPreparedStatementSetter对象的setValues()方法;
当doInPreparedStatement()中的代码执行到rse.extractData(rsToUse)时,会调用代码片段三中生成的ResultSetExtractor对象的extractData();
当代码片段三中的ResultSetExtractor对象的extractData()执行到this.rch.processRow(rs);时,回调方法,代码执行回到了在代码片段一中自定义实现RowCallbackHandler接口的processRow()方法中.
7.jdbctemplate的sql查询与结果集转换完成.

0 0
原创粉丝点击