首先解释一下何为回调?
我的理解是原本是A调用B,在方法执行过程中B又会调用由A提供的一个方法,故称回调。好处是可以让调用方来决定部分算法逻辑,这让整个算法变得非常灵活,但是控制权还是在提供方这边。
Spring的JdbcTemplate就是回调的典型实现,摘取 queryForObject() 进行分析
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException {
List<T> results = query(sql, args, new RowMapperResultSetExtractor<>(rowMapper, 1));
return DataAccessUtils.nullableSingleResult(results);
}
此时加入要查询出一个domian对象,通常会这么写
Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
(resultSet, rowNum) -> {
Actor newActor = new Actor();
newActor.setFirstName(resultSet.getString("first_name"));
newActor.setLastName(resultSet.getString("last_name"));
return newActor;
},
1212L);
第二个参数 RowMapper 就是一个回调函数
@FunctionalInterface
public interface RowMapper<T> {
/**
* Implementations must implement this method to map each row of data
* in the ResultSet. This method should not call {@code next()} on
* the ResultSet; it is only supposed to map values of the current row.
* @param rs the ResultSet to map (pre-initialized for the current row)
* @param rowNum the number of the current row
* @return the result object for the current row (may be {@code null})
* @throws SQLException if an SQLException is encountered getting
* column values (that is, there's no need to catch SQLException)
*/
@Nullable
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
所以本质上就是由我们来提供 mapRow 的具体实现,比如这里就是要生成一个 Actor 对象,然后 jdbcTemplate 回调 我们提供的这个实现,将这段逻辑插入到自己的方法中。当然你可以做任何事情,因为我们已经有了结果集 ResultSet,所以非常灵活。
具体执行过程可跟踪源码,最终都是进入到了 execute() 方法中,大概执行步骤都是
try{
获取 connection
...
执行我们提供的回调函数
...
}catch(){
}finally{
关闭 connection
释放资源
}