zoukankan      html  css  js  c++  java
  • SpringJDBC源码分析记录

    我们使用JdbcTemplate时,调用的query方法为:

    public <T> List<T> query(String sql, @Nullable Object[] args, RowMapper<T> rowMapper)

    把我们需要映射的自定义java类型传入到T中,也就是T的实际值为我们的类型,假设为User.我们传递的是RowMapper<User>,返回List<User>

    该方法调用到

    public <T> T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse)

    这里的T实际值为传递给ResultSetExtractor<T>的T值,后面会看到,最终传入接口类ResultSetExtractor<T>的实际T值为List<User>

    传入的第三个参数为:

    (ResultSetExtractor)(new RowMapperResultSetExtractor(rowMapper))

    此时rowMapper仍为我们传递的RowMapper<User>.

    再看RowMapperResultSetExtractor类:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //

    package org.springframework.jdbc.core;

    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    import org.springframework.util.Assert;

    public class RowMapperResultSetExtractor<T> implements ResultSetExtractor<List<T>> {
    private final RowMapper<T> rowMapper;
    private final int rowsExpected;

    public RowMapperResultSetExtractor(RowMapper<T> rowMapper) {
    this(rowMapper, 0);
    }

    public RowMapperResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {
    Assert.notNull(rowMapper, "RowMapper is required");
    this.rowMapper = rowMapper;
    this.rowsExpected = rowsExpected;
    }

    public List<T> extractData(ResultSet rs) throws SQLException {
    List<T> results = this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList();
    int var3 = 0;

    while(rs.next()) {
    results.add(this.rowMapper.mapRow(rs, var3++));
    }

    return results;
    }
    }

    构造方法为泛型方法:

        public RowMapperResultSetExtractor(RowMapper<T> rowMapper) {
    this(rowMapper, 0);
    }

    传入RowMapper<User>,那么传给这个类的形参T的实际值为User(注意不要与其他类签名中的T弄混,不同类的泛型参数相互独立,没联系)

    但该类的泛型签名中:

    RowMapperResultSetExtractor<T> implements ResultSetExtractor<List<T>>

    最终将这里的List<T>传入其接口类的泛型参数中,这里就是容易混淆的地方,重点--接口类签名:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //

    package org.springframework.jdbc.core;

    import java.sql.ResultSet;
    import java.sql.SQLException;
    import org.springframework.dao.DataAccessException;
    import org.springframework.lang.Nullable;

    @FunctionalInterface
    public interface ResultSetExtractor<T> {
    @Nullable
    T extractData(ResultSet var1) throws SQLException, DataAccessException;
    }

    这里实现类的List<T>被整体传入接口类的T,即接口类的T实际值为List<User>了,这个T不要与实现类的T混淆,这是不同类相互独立的泛型参数。

    注意这里实现类需要覆盖的方法返回值为T(接口类的T),即List<User>,即实现类中的List<T>(实现类的T)

    那么看实现类该方法实现:

        public List<T> extractData(ResultSet rs) throws SQLException {
    List<T> results = this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList();
    int var3 = 0;

    while(rs.next()) {
    results.add(this.rowMapper.mapRow(rs, var3++));
    }

    return results;
    }

    确实是List<T>(实现类的T,经历了List<T>整体传入接口类再以List<T>整体返回的过程)

    这里的rowMapper即我们传入的需要我们自己实现的RowMapper<User>,我们需要实现其mapRow方法。

    这里的实现逻辑是:获取java原生结果集ResultSet,使用while(rs.next())迭代获取的数据库表的每一行(仍以rs代表),mapRow方法只需实现自定义处理每一行即可,我们一般将各个列取出,设置到我们的类User里面完成映射。

    这里的RowMapper接口签名:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //

    package org.springframework.jdbc.core;

    import java.sql.ResultSet;
    import java.sql.SQLException;
    import org.springframework.lang.Nullable;

    @FunctionalInterface
    public interface RowMapper<T> {
    @Nullable
    T mapRow(ResultSet var1, int var2) throws SQLException;
    }

    实现时我们将User传入给T,使mapRow返回User,

    调用时,我们将User最终传递给RowMapperResultSetExtractor<T>的T,使用其

    List<T> extractData(ResultSet rs)

    实现方法,该方法使用循环,每次回调RowMapper的mapRow方法,将ResultSet的每一行数据以User返回,加入List<User>,达到返回List<User>的目的。

    弄清楚不同类的泛型形参相互独立(不同的T),和User作为泛型实参(以不同的方式,比如可能直接传入,可能List<T>实参传递给T形参)传递到不同类的泛型形参中,最终不同类的泛型方法(不同的T)返回多少与实参相关(但T值不一定都是原始的User,可能是User,可能不是User)的类型,相互调用时,再对这些实际类型进行匹配编码调用,返回即可。

    好的设计模式是分模块的。

    这里的query负责查询结果集模块,RowMapperResultSetExtractor的相关方法负责处理结果集模块;

    这些模块都有其接口类型,达到统一接口,扩展性实现;

    模块内部,主要方法(包括构造函数)有多个重载,和重载方法间调用,比如这里的query方法,达到内部进一步分工细化,细模块开发的结构;

    模块间调用采用回调方式,又充分实现了面向接口、模块化调用、模块间解耦、可扩展:

    子模块负责实现子模块接口的回调方法,比如这里的结果集处理实现类RowMapperResultSetExtractor<T>实现了ResultSetExtractor<T>接口的extractData方法,我们自定义并将实例传入到query方法的匿名内部类又实现了RowMapper<T>接口的mapRow方法,

    父模块调用子模块时,引用子模块的接口作为形参,子模块实例整体传递给父模块,父模块中调用的是统一的子模块接口方法,利用多态(动态绑定,运行时绑定),清晰实现了模块间解耦和模块组件的可扩展性,比如这里query调用ResultSetExtractor<T>接口的extractData方法,其实现类RowMapperResultSetExtractor<T>模块在实现其extractData方法时,又调用了RowMapper<T>接口的mapRow方法。

    总结起来:模块向上实现接口,向下调用接口,接口的实现是模块。这样一层一层的关系,层次分明,功能独立、细化、可复用,清晰解耦,组合简单,实现清晰。即使相互调用,或多个模块复杂组合,模块本身都是相对独立的(也基本相当于功能独立,可复用),模块间调用关系也面向接口,清晰可扩展,轻耦合。

    这种面向接口、模块化、层次化、轻耦合开发,清晰的源码架构,与Spring的增强功能IOC,DI,AOP有异曲同工之妙,可以说是一种最原始的、根本的解耦方法,再与这些功能进一步配合,分别从框架开发层面和用户应用级开发(直接使用Spring的人)层面简化了程序开发。

    这才是我们该从Spring源码中窥伺和学到的。

  • 相关阅读:
    用户与组
    初识linux
    权限管理
    认识vim 编辑器
    文件归档
    路由相关术语
    Access、Hybrid和Trunk
    #error作用
    交换芯片收发包的 DMA 实现原理
    linux网络学习
  • 原文地址:https://www.cnblogs.com/free-wings/p/9662052.html
Copyright © 2011-2022 走看看