关于 回调 的一些理解(以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查询与结果集转换完成.
- 关于 回调 的一些理解(以jdbctemplate为例)
- 对spring JdbcTemplate 代码的一些理解
- 关于多维数组的一点个人的理解(以三维数组为例)
- 关于在Spring4 jdbctemplate 遇到的一些基本问题总结
- 关于动态规划解题步骤和两个重要性质的理解---以最长递增子序列为例
- 我所理解的学习--以计算机为例
- 我所理解的研究--以计算机为例
- 理解PhotoView的核心,以双击事件为例
- 做登录的一些注意事项(以java为例)
- 关于栈的算法(以括号匹配为例)
- 关于inittab的解读(以RedHat版本为例
- 关于开发项目的注意事项-以javaEE为例
- 关于Android数据库升级的实践(以ormlite为例)
- 以java为例理解协变性
- jdbcTemplate的查询方法理解
- jdbcTemplate的理解及使用
- 关于JAXB的一些理解
- 关于bitmap的一些理解
- android——View绘制流程
- AHK找图找色功能的实现 和 素材的制作:
- Google 的开源技术protobuf 简介与例子
- Java正则表达式(下)
- RT-Thread-学习笔记2 添加串口设备
- 关于 回调 的一些理解(以jdbctemplate为例)
- 关于一个sql注入注入题目的思考
- Kinetis FTM的PWM、输入捕获、正交解码
- 算法训练 装箱问题(0-1背包)
- 设计模式之中介者设计模式n
- T.1047.编程团体赛
- mybatis 配置文件configuration environments以及单独使用mybatis的demo
- String、StringBuffer、与StringBuilder的区别
- 你还在为移动端选择器而捉急吗?【原创】