《Spring源码深度解析 郝佳 第2版》JDBC、MyBatis原理

292 阅读14分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

往期博客

《Spring源码深度解析 郝佳 第2版》容器的基本实现与XML文件的加载

《Spring源码深度解析 郝佳 第2版》XML标签的解析

《Spring源码深度解析 郝佳 第2版》bean的加载、循环依赖的解决

《Spring源码深度解析 郝佳 第2版》ApplicationContext

《Spring源码深度解析 郝佳 第2版》AOP

目录

  1. JDBC
    1. update方法实现原理
    2. execute方法真正执行逻辑
      • // 获取connection对象
      • 根据connection使用PreparedStatementCreator创建PreparedStatement
      • 设置DataSource填充的属性到PreparedStatement
      • 重载调用回调函数逻辑,此处是参数值、参数类型填充setValues到PreparedStatement和真正执行executeUpdate的逻辑
      • 处理警告
      • 释放连接
    3. query方法实现原理
  2. MyBatis

一、JDBC

JDBC是一种用于执行SQL语句的Java API ,可以为多种关系型数据库提供统一访问,它由一组用Java语言编写的类和接口组成,具体的以MySQL为例原生方式使用步骤

  1. 引入MySQL支持的驱动程序jar包,mysql-connector-java.xxx.jar
  2. 使用Class.forName("com.mysql.jdbc.Driver")加载驱动
  3. 创建数据库连接对象,DriverManager静态方法创建onnection对象,如不指定DataSource连接池的配置,就要在这里配置url和账号密码
  4. 使用Connection对象创建Statement对象,调用execuUpdate、executeQuery等方法,主要执行静态的SQL语句。
  5. 根据返回的ResultSet对象可以指向当前行数据的指针,可以使用next方法逐行获取,具体的行数据格式可以创建XxxRowMapper实现RowMapper然后重写mapRow(ResuleSet set,int index)方法,将set中对应数据库的列设置到POJO实例然后返回。
  6. 使用完关闭连接调用Connection的close方法

也可以使用封装的JdbcTemplate直接操作,他的构造参数可以传入DataSource实例,他是为了创建第三方连接池,然后直接调用系列方法

1.update方法的实现原理

在这里插入图片描述

在这里插入图片描述

update方法的7种重载

// JdbcTemplate

// 1. 只包含sql
 public int update(final String sql) throws DataAccessException {
        Assert.notNull(sql, "SQL must not be null");
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Executing SQL update [" + sql + "]");
        }

        class UpdateStatementCallback implements StatementCallback<Integer>, SqlProvider {
            UpdateStatementCallback() {
            }

            public Integer doInStatement(Statement stmt) throws SQLException {
                int rows = stmt.executeUpdate(sql);
                if (JdbcTemplate.this.logger.isTraceEnabled()) {
                    JdbcTemplate.this.logger.trace("SQL update affected " + rows + " rows");
                }

                return rows;
            }

            public String getSql() {
                return sql;
            }
        }

        return updateCount((Integer)this.execute((StatementCallback)(new UpdateStatementCallback())));
    }
// 2. 包含 PreparedStatementCreator 、 PreparedStatementSetter 
 protected int update(PreparedStatementCreator psc, @Nullable PreparedStatementSetter pss) throws DataAccessException {
        this.logger.debug("Executing prepared SQL update");
        return updateCount((Integer)this.execute(psc, (ps) -> {
            Integer var4;
            try {
                if (pss != null) {
                    pss.setValues(ps);
                }

                int rows = ps.executeUpdate();
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("SQL update affected " + rows + " rows");
                }

                var4 = rows;
            } finally {
                if (pss instanceof ParameterDisposer) {
                    ((ParameterDisposer)pss).cleanupParameters();
                }

            }

            return var4;
        }));
    }

// 3. 只包含 PreparedStatementCreator 
    public int update(PreparedStatementCreator psc) throws DataAccessException {
        return this.update(psc, (PreparedStatementSetter)null);
    }

// 4. 包含 PreparedStatementCreator 、generatedKeyHolder
    public int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder) throws DataAccessException {
        Assert.notNull(generatedKeyHolder, "KeyHolder must not be null");
        this.logger.debug("Executing SQL update and returning generated keys");
        return updateCount((Integer)this.execute(psc, (ps) -> {
            int rows = ps.executeUpdate();
            List<Map<String, Object>> generatedKeys = generatedKeyHolder.getKeyList();
            generatedKeys.clear();
            ResultSet keys = ps.getGeneratedKeys();
            if (keys != null) {
                try {
                    RowMapperResultSetExtractor<Map<String, Object>> rse = new RowMapperResultSetExtractor(this.getColumnMapRowMapper(), 1);
                    generatedKeys.addAll((Collection)result(rse.extractData(keys)));
                } finally {
                    JdbcUtils.closeResultSet(keys);
                }
            }

            if (this.logger.isTraceEnabled()) {
                this.logger.trace("SQL update affected " + rows + " rows and returned " + generatedKeys.size() + " keys");
            }

            return rows;
        }));
    }

// 5. 包含sql、PreparedStatementSetter 
    public int update(String sql, @Nullable PreparedStatementSetter pss) throws DataAccessException {
        return this.update((PreparedStatementCreator)(new JdbcTemplate.SimplePreparedStatementCreator(sql)), (PreparedStatementSetter)pss);
    }
// 6. 包含sql、args、argTypes
    public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {
        return this.update(sql, this.newArgTypePreparedStatementSetter(args, argTypes));
    }

// 7. 包含sql、args
    public int update(String sql, @Nullable Object... args) throws DataAccessException {
        return this.update(sql, this.newArgPreparedStatementSetter(args));
    }

2.execute方法真正执行逻辑

execute是真正的执行者,query、update方法都是调用的它,以包含sql、args、argTypes的update方法为例分析

public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {
		// 1. 重载,使用ArgTypePreparedStatementSetter封装参数值,这些值对应sql中占位“?”、参数类型
        return this.update(sql, this.newArgTypePreparedStatementSetter(args, argTypes));
    }
// 重载
public int update(String sql, @Nullable PreparedStatementSetter pss) throws DataAccessException {
		// 2. 重载,使用SimplePreparedStatementCreator封装sql
        return this.update((PreparedStatementCreator)(new JdbcTemplate.SimplePreparedStatementCreator(sql)), (PreparedStatementSetter)pss);
    }
// 重载
protected int update(PreparedStatementCreator psc, @Nullable PreparedStatementSetter pss) throws DataAccessException {
        this.logger.debug("Executing prepared SQL update");
        // 3. 真正的执行时execute方法
        return updateCount(
        (Integer)this.execute(psc, (ps) -> {
            Integer var4;
            try {
                if (pss != null) {
                	
                    pss.setValues(ps);
                }
				
                int rows = ps.executeUpdate();
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("SQL update affected " + rows + " rows");
                }

                var4 = rows;
            } finally {
                if (pss instanceof ParameterDisposer) {
                    ((ParameterDisposer)pss).cleanupParameters();
                }

            }

            return var4;
        }));
    }


在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

重载调用另外一个 execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action),因为回调函数是一个函数式接口,真正的执行是在这个回调函数里面的。 在这里插入图片描述

接下来详细分析这个

// JdbcTemplate

 @Nullable
    public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException {
        Assert.notNull(psc, "PreparedStatementCreator must not be null");
        Assert.notNull(action, "Callback object must not be null");
        
        if (this.logger.isDebugEnabled()) {
            String sql = getSql(psc);
            this.logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
        }
		// 1. 获取connection对象
        Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
        
        PreparedStatement ps = null;

        Object var13;
        try {
        	// 2. 要执行的ps,使用PreparedStatementCreator 创建 PreparedStatement
            ps = psc.createPreparedStatement(con);
            // 3. 设置用户指定的参数值到ps
            this.applyStatementSettings(ps);
            // 4. 回调函数doInPreparedStatement,也是真正执行的过程,获取执行结果result
            T result = action.doInPreparedStatement(ps);
			// 5. 处理警告
            this.handleWarnings((Statement)ps);
            var13 = result;
        } catch (SQLException var10) {
            if (psc instanceof ParameterDisposer) {
                ((ParameterDisposer)psc).cleanupParameters();
            }
			
            String sql = getSql(psc);
            psc = null;
            JdbcUtils.closeStatement(ps);
            ps = null;
            // 6.  释放连接
            DataSourceUtils.releaseConnection(con, this.getDataSource());
            con = null;
            throw this.translateException("PreparedStatementCallback", sql, var10);
        } finally {
            if (psc instanceof ParameterDisposer) {
                ((ParameterDisposer)psc).cleanupParameters();
            }

            JdbcUtils.closeStatement(ps);
            DataSourceUtils.releaseConnection(con, this.getDataSource());
        }

        return var13;
    }
// 1. 获取connection对象

委托DataSourceUtils去获取连接,实现在doGetConnection方法,该方法内部部不是简单的使用DataSource.getConnection获取连接的,还要考虑其他情况,主要是事务相关的一些处理。

// DataSourceUtils
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
        try {
        	// 调用
            return doGetConnection(dataSource);
        } catch (SQLException var2) {
            throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", var2);
        } catch (IllegalStateException var3) {
            throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + var3.getMessage());
        }
    }

// 委托
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        Assert.notNull(dataSource, "No DataSource specified");
        ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
        if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) {
            logger.debug("Fetching JDBC Connection from DataSource");

            Connection con = fetchConnection(dataSource);
			// 当前线程支持同步,就是在一个方法内部单线程串行调用
            if (TransactionSynchronizationManager.isSynchronizationActive()) {
                try {
					// 在同一事务中使用同一数据库连接connection
                    ConnectionHolder holderToUse = conHolder;
                    if (conHolder == null) {
                        holderToUse = new ConnectionHolder(con);
                    } else {
                        conHolder.setConnection(con);
                    }
					// 记录数据库连接
                    holderToUse.requested();
                    TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource));
                    holderToUse.setSynchronizedWithTransaction(true);
                    if (holderToUse != conHolder) {
                        TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
                    }
                } catch (RuntimeException var4) {
                	
                    releaseConnection(con, dataSource);
                    throw var4;
                }
            }

            return con;
        } else {
            conHolder.requested();
            if (!conHolder.hasConnection()) {
                logger.debug("Fetching resumed JDBC Connection from DataSource");
                conHolder.setConnection(fetchConnection(dataSource));
            }

            return conHolder.getConnection();
        }
    }
// 2. 根据connection使用PreparedStatementCreator 创建 PreparedStatement

在这里插入图片描述

// PreparedStatementCreatorFactory,包含内部类PreparedStatementCreatorImpl
// PreparedStatementCreatorImpl
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
            PreparedStatement ps;
            if (PreparedStatementCreatorFactory.this.generatedKeysColumnNames == null && !PreparedStatementCreatorFactory.this.returnGeneratedKeys) {
                if (PreparedStatementCreatorFactory.this.resultSetType == 1003 && !PreparedStatementCreatorFactory.this.updatableResults) {
                    ps = con.prepareStatement(this.actualSql);
                } else {
                    ps = con.prepareStatement(this.actualSql, PreparedStatementCreatorFactory.this.resultSetType, PreparedStatementCreatorFactory.this.updatableResults ? 1008 : 1007);
                }
            } else if (PreparedStatementCreatorFactory.this.generatedKeysColumnNames != null) {
                ps = con.prepareStatement(this.actualSql, PreparedStatementCreatorFactory.this.generatedKeysColumnNames);
            } else {
                ps = con.prepareStatement(this.actualSql, 1);
            }

            this.setValues(ps);
            return ps;
        }
// 3. 设置用户指定的DataSource值到PreparedStatement
 protected void applyStatementSettings(Statement stmt) throws SQLException {
 		// 主要是减少网络交互次数,访问ResultSet的时候,如果调用next一次从服务器取一条数据
 		// 就会产生大量开销,因此设置参数可以决定一次从数据库服务器取对少条数据放到内存缓存起来
        int fetchSize = this.getFetchSize();
        if (fetchSize != -1) {
            stmt.setFetchSize(fetchSize);
        }

        int maxRows = this.getMaxRows();
        if (maxRows != -1) {
            stmt.setMaxRows(maxRows);
        }

        DataSourceUtils.applyTimeout(stmt, this.getDataSource(), this.getQueryTimeout());
    }
// 4. 回调函数doInPreparedStatement,包含参数值的填充、真正的执行逻辑
  • // sql中对应占位符值的填充,主要是Spring做的封装,从而不用依次调用setXxx方法this.setValues(ps, lobCreator);,借助两个设置器ArgumentPreparedStatementSetter、ArgumentTypePreparedStatementSetter,他们都实现PreparedStatementSetter
  • // 真正执行 var3 = ps.executeUpdate();借助
// PreparedStatementCallback的实现类AbstractLobCreatingPreparedStatementCallback
public final Integer doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
        LobCreator lobCreator = this.lobHandler.getLobCreator();

        Integer var3;
        try {
        	// sql中对应占位符值的填充,主要是Spring做的封装,从而不用依次调用setXxx方法
            this.setValues(ps, lobCreator);
            // 真正执行
            var3 = ps.executeUpdate();
        } finally {
            lobCreator.close();
        }

        return var3;
    }
// setValues方法

// sql中对应占位符值的填充,主要是Spring做的封装,从而不用依次调用setXxx方法this.setValues(ps, lobCreator);,借助两个设置器ArgumentPreparedStatementSetter、ArgumentTypePreparedStatementSetter,他们都实现PreparedStatementSetter

在这里插入图片描述


// ArgumentPreparedStatementSetter 设置参数值
 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);
        }

    }

// ArgumentTypePreparedStatementSetter 设置参数类型
public void setValues(PreparedStatement ps) throws SQLException {
        int parameterPosition = 1;
        if (this.args != null && this.argTypes != null) {
            label45:
            for(int i = 0; i < this.args.length; ++i) {
                Object arg = this.args[i];
                if (arg instanceof Collection && this.argTypes[i] != 2003) {
                    Collection<?> entries = (Collection)arg;
                    Iterator var6 = entries.iterator();

                    while(true) {
                        while(true) {
                            if (!var6.hasNext()) {
                                continue label45;
                            }

                            Object entry = var6.next();
                            if (entry instanceof Object[]) {
                                Object[] valueArray = (Object[])((Object[])entry);
                                Object[] var9 = valueArray;
                                int var10 = valueArray.length;

                                for(int var11 = 0; var11 < var10; ++var11) {
                                    Object argValue = var9[var11];
                                    this.doSetValue(ps, parameterPosition, this.argTypes[i], argValue);
                                    ++parameterPosition;
                                }
                            } else {
                                this.doSetValue(ps, parameterPosition, this.argTypes[i], entry);
                                ++parameterPosition;
                            }
                        }
                    }
                } else {
                    this.doSetValue(ps, parameterPosition, this.argTypes[i], arg);
                    ++parameterPosition;
                }
            }
        }

    }

    protected void doSetValue(PreparedStatement ps, int parameterPosition, int argType, Object argValue) throws SQLException {
        StatementCreatorUtils.setParameterValue(ps, parameterPosition, argType, argValue);
    }
// executeUpdate方法

在这里插入图片描述

// ProxyPreparedStatement实现PreparedStatement
public int executeUpdate() throws SQLException {
        this.connection.markCommitStateDirty();
        return ((PreparedStatement)this.delegate).executeUpdate();
    }
// 5. 处理警告

protected void handleWarnings(Statement stmt) throws SQLException {
		// 设置为忽略警告时只打印日志
        if (this.isIgnoreWarnings()) {
            if (this.logger.isDebugEnabled()) {
            // 如果日志开始的情况下打印日志
                for(SQLWarning warningToLog = stmt.getWarnings(); warningToLog != null; warningToLog = warningToLog.getNextWarning()) {
                    this.logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + "', error code '" + warningToLog.getErrorCode() + "', message [" + warningToLog.getMessage() + "]");
                }
            }
        } else {
            this.handleWarnings(stmt.getWarnings());
        }

    }

    protected void handleWarnings(@Nullable SQLWarning warning) throws SQLWarningException {
        if (warning != null) {
            throw new SQLWarningException("Warning not ignored", warning);
        }
    }

// 6. 释放连接

这里的释放连接同样考虑到事务的问题,如果当前线程下面还存在存在事务,因为使用的是一个connection因此需要根据前面的计数减1,因此不是简单的connection.close(),而是进行逻辑事务数量减1

 public static void releaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) {
        try {
            doReleaseConnection(con, dataSource);
        } catch (SQLException var3) {
            logger.debug("Could not close JDBC Connection", var3);
        } catch (Throwable var4) {
            logger.debug("Unexpected exception on closing JDBC Connection", var4);
        }

    }

    public static void doReleaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) throws SQLException {
        if (con != null) {
            if (dataSource != null) {
                ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
                if (conHolder != null && connectionEquals(conHolder, con)) {
                    conHolder.released();
                    return;
                }
            }
			// 真正关闭connection
            doCloseConnection(con, dataSource);
        }
    }

3.query方法的实现原理

基本逻辑和update方法一致,只不过是回调函数传递的不同 在这里插入图片描述

然后就是重载调用

// 主要是定义回调方法,然后最后在调用回调函数中的执行逻辑
 @Nullable
    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
        Assert.notNull(sql, "SQL must not be null");
        Assert.notNull(rse, "ResultSetExtractor must not be null");
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Executing SQL query [" + sql + "]");
        }
		
		// 定义内部类,作为回调传入下面调用
        class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
            QueryStatementCallback() {
            }

            @Nullable
            public T doInStatement(Statement stmt) throws SQLException {
                ResultSet rs = null;

                Object var3;
                try {
                    rs = stmt.executeQuery(sql);
                    var3 = rse.extractData(rs);
                } finally {
                    JdbcUtils.closeResultSet(rs);
                }

                return var3;
            }

            public String getSql() {
                return sql;
            }
        }

		// 回调调用入口
        return this.execute((StatementCallback)(new QueryStatementCallback()));
    }
    
// 回调调用入口
 @Nullable
    public <T> T execute(StatementCallback<T> action) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");
        Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
        Statement stmt = null;

        Object var11;
        try {
            stmt = con.createStatement();
            this.applyStatementSettings(stmt);
            // 回调真正调用,也就是上面的回调方法
            T result = action.doInStatement(stmt);
            
            this.handleWarnings(stmt);
            var11 = result;
        } catch (SQLException var9) {
            String sql = getSql(action);
            JdbcUtils.closeStatement(stmt);
            stmt = null;
            DataSourceUtils.releaseConnection(con, this.getDataSource());
            con = null;
            throw this.translateException("StatementCallback", sql, var9);
        } finally {
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, this.getDataSource());
        }

        return var11;
    }

在这里插入图片描述

而对于第二步的映射 在这里插入图片描述

在这里插入图片描述

二、MyBatis

MyBatis是支持普通SQL查询、存储过程和高级映射的持久层框架。消除了JDBC所有的JDBC代码和参数的手工设置以及结果集的检索,使用简单的XML或者注解用于配置原始映射,将接口和Java的POJOS映射成数据库中的记录

可以不集成Spring的情况下单独使用

  1. 建立PO
  2. 建立XxxMapper接口,包含crud方法
  3. 建立配置文件,如数据源dataSource、环境environment、事务管理器transcationManager、mappers映射文件的路径
  4. 建立映射文件,主要是将sql和Mapper接口方法映射
  5. 建立测试类

整合Spring的情况下使用

  1. 建立PO
  2. 建立XxxMapper接口,包含crud方法
  3. 在Spring的xml配置文件中配置DataSource、SqlSessionFactoryBean、MapperFactoryBean类型的的bean
  4. 在MyBatis的xml配置文件配置映射文件,mappers映射文件的路径
  5. 建立映射文件,主要讲sql和Mapper接口方法映射
  6. 建立测试类

其中MyBatis的重要的几个类

  • DataSource:主要是数据源的一些配置,包括连接池等
  • SqlSessionFactoryBean:主要是创建SqlSessionFactory,它是MyBatis所有功能的基础,在他的子标签<property>配置的DataSource、ConfigLocation、pluhs等配置都需要初始化SqlSessionFactory传入。
  • MapperFactoryBean:主要是创建XxxMapper代理实例,以便调用相应的sql,整合Spring之后可以getBean的方式获取,就是内部封装了MapperFactoryBean的一些列逻辑
  • MapperScannerConfigurer:配置在applicationContext的XxxMapper接口信息需在<property>标签指定全类名和sqlSessionFactory。如果很多就不太方便了,因此需要一个自动扫描包下的XxxMapper功能,直接配置MapperScannerConfigurer类型的bean在<property>标签指定包路径即可

1.SqlSessionFactoryBean原理分析

它的继承体系 在这里插入图片描述

  • 实现了FactoryBean:实现此接口的bean在调用getBean方法会返回getObject方法返回的我们需要的bean。
  • 实现了InitilzationBean:实现此接口的bean会在初始化之后调用afterPropertiesSet方法进行bean的逻辑初始化。其中SqlSessionFactory就是在这里做的初始化
// 1. SqlSessionFactory的初始化的buildSqlSessionFactory方法

// 实现InitalizatioBean接口之后,会在SqlSessionFactoryBean类型的bean初始化完成之后调用afterPropertiesSet方法进行sqlSessionFactory 属性初始化

// SqlSessionFactoryBean的入口方法 afterPropertiesSet

 public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.dataSource, "Property 'dataSource' is required");
        Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
        // 调用 buildSqlSessionFactory
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

该方法内部就是创建Configuration实例,将各种配置设置到内部,由此可见尽管把Spring的配置文件和MyBatis的配置文件分开,但是在Spring配置文件中依然可以配置MyBatis的配置

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
        XMLConfigBuilder xmlConfigBuilder = null;
        Configuration targetConfiguration;
        
        Optional var10000;
       
       // configuration标签配置
        if (this.configuration != null) {
            targetConfiguration = this.configuration;
            if (targetConfiguration.getVariables() == null) {
                targetConfiguration.setVariables(this.configurationProperties);
            } else if (this.configurationProperties != null) {
                targetConfiguration.getVariables().putAll(this.configurationProperties);
            }
        } else if (this.configLocation != null) {
            xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);
            // configLocation标签配置,一般用于在Spring配置文件指定MyBatis配置文件地址,用于逻辑分离
            targetConfiguration = xmlConfigBuilder.getConfiguration();
        } else {
            LOGGER.debug(() -> {
                return "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration";
            });
            targetConfiguration = new Configuration();
            var10000 = Optional.ofNullable(this.configurationProperties);
            Objects.requireNonNull(targetConfiguration);
            var10000.ifPresent(targetConfiguration::setVariables);
        }

        var10000 = Optional.ofNullable(this.objectFactory);
        Objects.requireNonNull(targetConfiguration);
        var10000.ifPresent(targetConfiguration::setObjectFactory);
        var10000 = Optional.ofNullable(this.objectWrapperFactory);
        Objects.requireNonNull(targetConfiguration);
        var10000.ifPresent(targetConfiguration::setObjectWrapperFactory);
        var10000 = Optional.ofNullable(this.vfs);
        Objects.requireNonNull(targetConfiguration);
        var10000.ifPresent(targetConfiguration::setVfsImpl);
        Stream var24;
        // 1. typeAliasesPackage
        if (StringUtils.hasLength(this.typeAliasesPackage)) {
            var24 = this.scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream().filter((clazz) -> {
                return !clazz.isAnonymousClass();
            }).filter((clazz) -> {
                return !clazz.isInterface();
            }).filter((clazz) -> {
                return !clazz.isMemberClass();
            });
            TypeAliasRegistry var10001 = targetConfiguration.getTypeAliasRegistry();
            Objects.requireNonNull(var10001);
            var24.forEach(var10001::registerAlias);
        }
		// 2. typeAliases
        if (!ObjectUtils.isEmpty(this.typeAliases)) {
            Stream.of(this.typeAliases).forEach((typeAlias) -> {
                targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
                LOGGER.debug(() -> {
                    return "Registered type alias: '" + typeAlias + "'";
                });
            });
        }
		// 3. plugins
        if (!ObjectUtils.isEmpty(this.plugins)) {
            Stream.of(this.plugins).forEach((plugin) -> {
                targetConfiguration.addInterceptor(plugin);
                LOGGER.debug(() -> {
                    return "Registered plugin: '" + plugin + "'";
                });
            });
        }
		// 4. typeHandlersPackage
        if (StringUtils.hasLength(this.typeHandlersPackage)) {
            var24 = this.scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter((clazz) -> {
                return !clazz.isAnonymousClass();
            }).filter((clazz) -> {
                return !clazz.isInterface();
            }).filter((clazz) -> {
                return !Modifier.isAbstract(clazz.getModifiers());
            });
            TypeHandlerRegistry var25 = targetConfiguration.getTypeHandlerRegistry();
            Objects.requireNonNull(var25);
            var24.forEach(var25::register);
        }
		// 5. typeHandlers
        if (!ObjectUtils.isEmpty(this.typeHandlers)) {
            Stream.of(this.typeHandlers).forEach((typeHandler) -> {
                targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
                LOGGER.debug(() -> {
                    return "Registered type handler: '" + typeHandler + "'";
                });
            });
        }
        targetConfiguration.setDefaultEnumTypeHandler(this.defaultEnumTypeHandler);
        
        // 6. scriptingLanguageDrivers
        if (!ObjectUtils.isEmpty(this.scriptingLanguageDrivers)) {
            Stream.of(this.scriptingLanguageDrivers).forEach((languageDriver) -> {
                targetConfiguration.getLanguageRegistry().register(languageDriver);
                LOGGER.debug(() -> {
                    return "Registered scripting language driver: '" + languageDriver + "'";
                });
            });
        }

        var10000 = Optional.ofNullable(this.defaultScriptingLanguageDriver);
        Objects.requireNonNull(targetConfiguration);
        var10000.ifPresent(targetConfiguration::setDefaultScriptingLanguage);
        if (this.databaseIdProvider != null) {
            try {
                targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
            } catch (SQLException var23) {
                throw new NestedIOException("Failed getting a databaseId", var23);
            }
        }

        var10000 = Optional.ofNullable(this.cache);
        Objects.requireNonNull(targetConfiguration);
        var10000.ifPresent(targetConfiguration::addCache);
        if (xmlConfigBuilder != null) {
            try {
                xmlConfigBuilder.parse();
                LOGGER.debug(() -> {
                    return "Parsed configuration file: '" + this.configLocation + "'";
                });
            } catch (Exception var21) {
                throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var21);
            } finally {
                ErrorContext.instance().reset();
            }
        }
		// 7. environment
        targetConfiguration.setEnvironment(new Environment(this.environment, (TransactionFactory)(this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory), this.dataSource));
        if (this.mapperLocations != null) {
            if (this.mapperLocations.length == 0) {
                LOGGER.warn(() -> {
                    return "Property 'mapperLocations' was specified but matching resources are not found.";
                });
            } else {
                Resource[] var3 = this.mapperLocations;
                int var4 = var3.length;

                for(int var5 = 0; var5 < var4; ++var5) {
                    Resource mapperLocation = var3[var5];
                    if (mapperLocation != null) {
                        try {
                            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
                            xmlMapperBuilder.parse();
                        } catch (Exception var19) {
                            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var19);
                        } finally {
                            ErrorContext.instance().reset();
                        }

                        LOGGER.debug(() -> {
                            return "Parsed mapper file: '" + mapperLocation + "'";
                        });
                    }
                }
            }
        } else {
            LOGGER.debug(() -> {
                return "Property 'mapperLocations' was not specified.";
            });
        }
		// 调用
        return this.sqlSessionFactoryBuilder.build(targetConfiguration);
    }

	

其中SqlSession是一个接口,作为接口创建映射器代理 的接触类,内部包含各种crud、commit、rollback的定义,在setSqlSession的setSqlSessionFactory方法中最终封装的是DefaultSqlSessionFactory到成员变量sqlSession

在这里插入图片描述

// DefaultSqlSessionFactory 继承 SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
// 构造函数
 public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }
// 2. SqlSessionFactory的获取

// 实现了FactoryBean接口,会在在调用getBean方法会返回getObject方法返回的我们需要的bean,没有初始化的时候进行afterPropertiesSet初始化。

// 重写getObject方法,也就是在getBean的时候完成 afterPropertiesSet 调用初始化sqlSessionFactory
    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
        	// 初始化sqlSessionFactory入口
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }

2.MapperFactoryBean原理分析

假如我们定义UserMapper接口内部包含getUser方法,然后

  • 单独使用MyBatis的时候是UserMapper up = sqlSession.getMapper(UserMapper.class)
  • 整合Spring之后可以使用UserMapper up =(Usermapper) context.getBean("userMapper"),它的原理就是对上面的在一次封装

所以内部逻辑在MapperFactoryBean中实现

// 1.MapperFactory的初始化

先看继承体系 在这里插入图片描述

和上面类似,它实现InitalizationBean接口,所以初始化方法在afterPropertiesSet方法,在DaoSupport中实现,也就initDao方法

 public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
 		// 1. 验证DAO配置,由MapperFactoryBean实现,核心逻辑
        this.checkDaoConfig();

        try {
        	// 2. 模板方法,留给子类拓展
            this.initDao();
        } catch (Exception var2) {
            throw new BeanInitializationException("Initialization of DAO failed", var2);
        }
    }
// 1. 验证DAO配置checkDaoConfig,由MapperFactoryBean实现

主要进行的验证

  • 父类SqlSessionDaoSupport对sqlSession不为空的验证,也就是不开启自动扫描XxxMapper的时候,在每个映射文件xml配置XxxMapper的标签都应该设置sqlSessionFactory,不配置就会报错
  • 映射接口的验证
  • 映射文件存在性验证,防止出现XxxMapper无对应映射接口

主要是将所有配置的XxxMapper接口放到configuration属性中,这里也可以看出Spring配置、MyBatis配置文件分离并不影响

 protected void checkDaoConfig() {
 		// 父类SqlSessionDaoSupport实现
        super.checkDaoConfig();
        Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
        Configuration configuration = this.getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
            try {
            	// 添加XxxMapper到SqlSessionFactoryBean的configuration属性
                configuration.addMapper(this.mapperInterface);
            } catch (Exception var6) {
                this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
                throw new IllegalArgumentException(var6);
            } finally {
                ErrorContext.instance().reset();
            }
        }

    }
// 2.MapperFactory的获取
//MapperFactoryBean
 public T getObject() throws Exception {
        return this.getSqlSession().getMapper(this.mapperInterface);
    }

// SqlSessionDaoSupport的getSqlSession方法
 public SqlSession getSqlSession() {
        return this.sqlSessionTemplate;
    }
// 可见 sqlSessionTemplate 成员属性封装了  sqlSessionFactory
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

3.MapperScannerConfigurer原理分析

继承体系

在这里插入图片描述

// MapperScannerConfigurer

 private boolean processPropertyPlaceHolders;

 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
        	// 1. 关键地方,如在xml中取出properties配置的basePackge值
            this.processPropertyPlaceHolders();
        }
		// 2.  初始化扫描器ClassPathMapperScanner
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(this.lazyInitialization)) {
            scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
        }

        if (StringUtils.hasText(this.defaultScope)) {
            scanner.setDefaultScope(this.defaultScope);
        }
		// 3. 注册对应的过滤器
        scanner.registerFilters();
        //4.  开始扫描Java文件
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }
// 1. 关键地方processPropertyPlaceHolders,如在xml中取出properties配置的basePackge值

往期博客对postProcessBeanFactory、PropertyPlaceholderConfigurer的分析 在这里插入图片描述

  • BeanDefinitionRegistries会在应用启动的时候调用registryBeanDefinitions方法注册BeanDefinition,早于BeanFactoryPostProcessors的调用。

  • PropertyPlaceholderConfigurer将properties文件中的值取出到xml配置文件的原理是实现了BeanFactoryPostProcessors的postProcessBeanFactory,因此正常情况会在registryBeanDefinitions的后面,导致注册BeanDefinition时候xml中还没取出properties配置内容,自然映射不到BeanDefintion中了

因此MyBatis的解决就是使用注册的MapperScannerConfigurer提前借助PropertyResourceConfigurer的processPropertyPlaceHolders取出值注册BeanDefinition

  1. 找出所有配置在xml中的PropertyResourceConfigurer类型的bean
  2. 模拟Spring环境来使用mapperScannerBean类型的Bean提前调用后处理器postProcessBeanFactory,进而调用PropertyResourceConfigurer处理器值提取保存到成员变量,然后设置到scanner中等下真正注册BeanDefinition
private void processPropertyPlaceHolders() {
        Map<String, PropertyResourceConfigurer> prcs = this.applicationContext.getBeansOfType(PropertyResourceConfigurer.class, false, false);
        
        if (!prcs.isEmpty() && this.applicationContext instanceof ConfigurableApplicationContext) {
            BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext)this.applicationContext).getBeanFactory().getBeanDefinition(this.beanName);
			
			// 虚拟 DefaultListableBeanFactory ,为了营造factory环境,完成调用PropertyResourceConfigurer处理器就失效
            DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
            // 提前取值
            factory.registerBeanDefinition(this.beanName, mapperScannerBean);
            Iterator var4 = prcs.values().iterator();

            while(var4.hasNext()) {
                PropertyResourceConfigurer prc = (PropertyResourceConfigurer)var4.next();
                // 接着调用 postProcessBeanFactory,进而调用PropertyResourceConfigurer处理器完成值取出
                prc.postProcessBeanFactory(factory);
            }
			// 取出值
            PropertyValues values = mapperScannerBean.getPropertyValues();
            
            this.basePackage = this.getPropertyValue("basePackage", values);
            this.sqlSessionFactoryBeanName = this.getPropertyValue("sqlSessionFactoryBeanName", values);
            this.sqlSessionTemplateBeanName = this.getPropertyValue("sqlSessionTemplateBeanName", values);
            this.lazyInitialization = this.getPropertyValue("lazyInitialization", values);
            this.defaultScope = this.getPropertyValue("defaultScope", values);
        }

		// 真实值使用保存到成员变量
        Optional var10001 = Optional.ofNullable(this.basePackage);
        Environment var10002 = this.getEnvironment();
        Objects.requireNonNull(var10002);
        this.basePackage = (String)var10001.map(var10002::resolvePlaceholders).orElse((Object)null);
        var10001 = Optional.ofNullable(this.sqlSessionFactoryBeanName);
        var10002 = this.getEnvironment();
        Objects.requireNonNull(var10002);
        this.sqlSessionFactoryBeanName = (String)var10001.map(var10002::resolvePlaceholders).orElse((Object)null);
        var10001 = Optional.ofNullable(this.sqlSessionTemplateBeanName);
        var10002 = this.getEnvironment();
        Objects.requireNonNull(var10002);
        this.sqlSessionTemplateBeanName = (String)var10001.map(var10002::resolvePlaceholders).orElse((Object)null);
        var10001 = Optional.ofNullable(this.lazyInitialization);
        var10002 = this.getEnvironment();
        Objects.requireNonNull(var10002);
        this.lazyInitialization = (String)var10001.map(var10002::resolvePlaceholders).orElse((Object)null);
        var10001 = Optional.ofNullable(this.defaultScope);
        var10002 = this.getEnvironment();
        Objects.requireNonNull(var10002);
        this.defaultScope = (String)var10001.map(var10002::resolvePlaceholders).orElse((Object)null);
    }
//4. scan方法,开始扫描Java文件
// ClassPathBeanDefinitionScanner
 public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        // 真正扫描
        this.doScan(basePackages);
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }

        return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
    }

doScan真正扫描

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        // 需要注册的XxxMapper的BeanDefintion
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
        String[] var3 = basePackages;
        int var4 = basePackages.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String basePackage = var3[var5];
            // 扫描basePackage包下的java文件,也就是XxxMapper
            Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
            Iterator var8 = candidates.iterator();
			// 遍历
            while(var8.hasNext()) {
                BeanDefinition candidate = (BeanDefinition)var8.next();
                
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                // 设置scope属性
                candidate.setScope(scopeMetadata.getScopeName());
                
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                
                if (candidate instanceof AbstractBeanDefinition) {
                    this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
                }
				// 如果是AnnotatedBeanDefinition 类型,需要检查常用注解如Lazy、Primary
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
                }

                if (this.checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    // 如果当前bean需要生成代理需要进一步处理
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    // 注册
                    this.registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }

        return beanDefinitions;
    }
    
// scanCandidateComponents方法

 private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
        LinkedHashSet candidates = new LinkedHashSet();

        try {
            String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern;
            Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath);
            boolean traceEnabled = this.logger.isTraceEnabled();
            boolean debugEnabled = this.logger.isDebugEnabled();
            Resource[] var7 = resources;
            int var8 = resources.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                Resource resource = var7[var9];
                if (traceEnabled) {
                    this.logger.trace("Scanning " + resource);
                }

                if (resource.isReadable()) {
                    try {
                        MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource);
                        if (this.isCandidateComponent(metadataReader)) {
                            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                            sbd.setSource(resource);
                            // 判断扫描文件是否符合要求
                            if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) {
                                if (debugEnabled) {
                                    this.logger.debug("Identified candidate component class: " + resource);
                                }

                                candidates.add(sbd);
                            } else if (debugEnabled) {
                                this.logger.debug("Ignored because not a concrete top-level class: " + resource);
                            }
                        } else if (traceEnabled) {
                            this.logger.trace("Ignored because not matching any filter: " + resource);
                        }
                    } catch (Throwable var13) {
                        throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var13);
                    }
                } else if (traceEnabled) {
                    this.logger.trace("Ignored because not readable: " + resource);
                }
            }

            return candidates;
        } catch (IOException var14) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var14);
        }
    }