以前一起没有很好的理解 “回调”,偶然的机会下,看了一下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查询与结果集转换完成.