首先解释一下何为回调?
我的理解是原本是A调用B,在方法执行过程中B又会调用由A提供的一个方法,故称回调。好处是可以让调用方来决定部分算法逻辑,这让整个算法变得非常灵活,但是控制权还是在提供方这边。
Spring的 JdbcTemplate
就是回调的典型实现,摘取 queryForObject()
进行分析
1
2
3
4
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
对象,通常会这么写
1
2
3
4
5
6
7
8
9
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
就是一个回调函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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() 方法中,大概执行步骤都是
1
2
3
4
5
6
7
8
9
10
11
12
13
try{
获取 connection
...
执行我们提供的回调函数
...
}catch(){
}finally{
关闭 connection
释放资源
}