Mybatis源码解析

Mybatis源码解析

Mybatis源码编译

先要下载mybatis的源码包,从github下clone或者下载zip包,需要下载两个,分别是mybatis和mybatis-parent,下载mybatis-parent时要先看看mybatis指定的parent包的版本。先导入mybatis-parent,利用IDEA的MAVEN插件进行构建。然后在导入mybatis,同理。其实就像平常从git上clone一些maven项目下来一样,没什么不同。
最后如图:
在这里插入图片描述

测试案例

在mybatis-master包下新建一个module,然后pom.xml中引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mybatis</artifactId>
        <groupId>org.mybatis</groupId>
        <version>3.3.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mybatis-test</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.3.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.21.0-GA</version>
        </dependency>
        <dependency>
            <groupId>ognl</groupId>
            <artifactId>ognl</artifactId>
            <version>2.7.3</version>
        </dependency>
    </dependencies>
</project>

resource下新建mybatis-config.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--配置Author.xml里面的返回类型 -->
    <typeAliases>
        <package name="com.mybatis.entity"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <!--配置获取数据库连接实例的数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url"
                          value="jdbc:mysql://localhost:3306/tedu_ums?characterEncoding=utf8" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>

mapper/UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
        "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<mapper namespace="com.mybatis.mapper.UserMapper">
    <insert id="addUser" parameterType="com.mybatis.entity.User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO t_user
          (username,password,phone,email)
        VALUES
          (#{username},#{password},#{phone},#{email})
    </insert>

    <!--/**
     * 根据用户名查询用户
     * @param username
     * @return
     */
    User findUserByUsername(String username);-->
    <select id="findUserByUsername" resultType="com.mybatis.entity.User">
        SELECT
          id,username,password,phone,email
        FROM
          t_user
        WHERE
          username = #{username}
    </select>

    <!--/**
     * 根据id查询用户数据
     * @param id 用户id
     * @return
     */
    User findUserById(Integer id);-->
    <select id="findUserById" resultType="com.mybatis.entity.User">
        SELECT
          id,username,password,phone,email
        FROM
          t_user
        WHERE
          id = #{id}
    </select>

    <!--/**
     * 查询所有用户
     * @return
     */
    List<User> findAllUser();-->
    <select id="findAllUser" resultType="com.mybatis.entity.User">
        SELECT
          id,username,password,phone,eamil
        FROM
          t_user
    </select>

    <!--/**
     * 根据id删除用户数据
     * @param id
     * @return
     */
    Integer delete(Integer id);-->
    <delete id="delete">
        DELETE FROM t_user
          WHERE id = #{id}
    </delete>

    <!--/**-->
    <!--* 根据id修改用户信息,不允许修改用户名-->
    <!--* @param user-->
    <!--* @return-->
    <!--*/-->
    <!--Integer updateUserInfo(User user);-->
    <update id="updateUserInfo" parameterType="com.mybatis.entity.User">
        UPDATE t_user SET
          password = #{password},
          phone = #{phone},
          email = #{email}
        WHERE
          id = #{id}
    </update>

    <!--/**
     * 根据id修改用户信息,不允许修改用户名
     * @param user
     * @return
     */
    Integer update(User user);-->
    <update id="update" parameterType="com.mybatis.entity.User">
        UPDATE t_user SET
        <if test="password != null">
            password = #{password},
        </if>
        <if test="phone != null">
            phone = #{phone},
        </if>
        <if test="email != null">
            email = #{email},
        </if>
          id=#{id}
        WHERE
          id = #{id}
    </update>
</mapper>

实体类

package com.mybatis.entity;

import java.io.Serializable;

public class User implements Serializable {

    private Integer id;
    private String username;
    private String password;
    private String phone;
    private String email;

    public User() {
    }

    public User(Integer id, String username, String password, String phone, String email) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.phone = phone;
        this.email = email;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (id != null ? !id.equals(user.id) : user.id != null) return false;
        if (username != null ? !username.equals(user.username) : user.username != null) return false;
        if (password != null ? !password.equals(user.password) : user.password != null) return false;
        if (phone != null ? !phone.equals(user.phone) : user.phone != null) return false;
        return email != null ? email.equals(user.email) : user.email == null;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (username != null ? username.hashCode() : 0);
        result = 31 * result + (password != null ? password.hashCode() : 0);
        result = 31 * result + (phone != null ? phone.hashCode() : 0);
        result = 31 * result + (email != null ? email.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

接口

package com.mybatis.mapper;


import com.mybatis.entity.User;

import java.util.List;

/**
 * Created by lenovo on 2019/2/10.
 */
public interface UserMapper {

    /**
     *新增用户
     * @param user
     */
    Integer addUser(User user);

    /**
     * 根据用户名查询用户
     * @param username
     * @return
     */
    User findUserByUsername(String username);

    /**
     * 根据id查询用户数据
     * @param id 用户id
     * @return
     */
    User findUserById(Integer id);

    /**
     * 查询所有用户
     * @return
     */
    List<User> findAllUser();

    /**
     * 根据id删除用户数据
     * @param id
     * @return
     */
    Integer delete(Integer id);

    /**
     * 根据id修改用户信息,不允许修改用户名
     * @param user
     * @return
     */
    @Deprecated
    Integer updateUserInfo(User user);

    /**
     * 根据id修改用户信息,不允许修改用户名
     * @param user
     * @return
     */
    Integer update(User user);

}

程序入口

package com.mybatis.main;

import com.mybatis.entity.User;
import com.mybatis.mapper.UserMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class _Main {
    public static void main(String[] args) {
        // 通过获取对象当前的类类型获取当前类型的类加载器来加载类类路径下的资源文件以此构建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(_Main.class.getClassLoader().getResourceAsStream("mybatis-config.xml"));
        // 创建SqlSession实例来自行数据库的所有方法和sql语句
        SqlSession session = sessionFactory.openSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.findUserByUsername("admin");
        // 使用完之后关闭sqlSession数据库资源
        session.close();
        System.out.println("user:"+user);
    }
}

点击运行,成功跑起
在这里插入图片描述
注意要联网,不然会报“Cause: java.net.UnknownHostException: mybatis.org”这样的错

源码分析

创建SqlSessionFactory

首先看测试程序的第一行代码

SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(_Main.class.getClassLoader().getResourceAsStream("mybatis-config.xml"));

先new一个SqlSessionFactoryBuilder,然后调用他的build方法,创建一个SqlSessionFactory 工厂
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream)

  public SqlSessionFactory build(InputStream inputStream) {
  	// 调用重载的build方法
    return build(inputStream, null, null);
  }

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        /*通过inputStream,就是我们的配置文件mybatis-config.xml的流对象,获取XMLConfigBuilder*/
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      /*XMLConfigBuilder的parse方法,返回一个Configuration对象*/
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)

  //最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }
  ...
}

Configuration 就包含了我们在配置文件中配置的信息
在这里插入图片描述

在这里插入图片描述

创建SqlSession

测试案例的第二行代码

SqlSession session = sessionFactory.openSession();

sessionFactory.openSession()里面会调用重载的openSessionFromDataSource方法

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //通过事务工厂来产生一个事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //生成一个执行器(事务包含在执行器里)
      final Executor executor = configuration.newExecutor(tx, execType);
      //然后产生一个DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      //如果打开事务出错,则关闭它
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      //最后清空错误上下文
      ErrorContext.instance().reset();
    }
  }

下面要创建SqlSession的执行器。在 Mybatis 配置⽂件中,可以指定默认的 ExecutorType 执⾏器类型,也可以⼿动给
DefaultSqlSessionFactory 的创建 SqlSession 的⽅法传递 ExecutorType 类型参数
org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)

  //产生执行器
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    //这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
    if (ExecutorType.BATCH == executorType) {
      /**
       * BatchExecutor:执⾏ update(没有 select,JDBC 批处理不⽀持 select),将所有 sql 都添加
       * 到批处理中(addBatch()),等待统⼀执⾏(executeBatch()),它缓存了多个 Statement 对象,每
       * 个 Statement 对象都是 addBatch()完毕后,等待逐⼀执⾏ executeBatch()批处理。与 JDBC 批处理
       * 相同
       */
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      /**
       * ReuseExecutor:执⾏ update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使
       * ⽤,不存在就创建,⽤完后,不关闭 Statement 对象,⽽是放置于 Map<String, Statement>内,供下
       * ⼀次使⽤。简⾔之,就是重复使⽤ Statement 对象
       */
      executor = new ReuseExecutor(this, transaction);
    } else {
      /*SimpleExecutor:每执⾏⼀次 update 或 select,就开启⼀个 Statement 对象,⽤完⽴刻关闭Statement 对象*/
      executor = new SimpleExecutor(this, transaction);
    }
    //如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //此处调用插件,通过插件可以改变Executor行为
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

创建SqlSession,类型是DefaultSqlSession,里面包含了配置信息,以及他的执行器,还有是否自动提交。一个SqlSession对应一个Executor ,Executor限制在 SqlSession ⽣命周期范围内。

org.apache.ibatis.session.defaults.DefaultSqlSession#DefaultSqlSession(org.apache.ibatis.session.Configuration, org.apache.ibatis.executor.Executor, boolean)

  /**
   * 是否自动提交
   */
  private boolean autoCommit;
  private boolean dirty;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

获取Mapper接口的实现类对象

测试案例的第三行代码

UserMapper mapper = session.getMapper(UserMapper.class);

org.apache.ibatis.session.SqlSession#getMapper

  /**
   * Retrieves a mapper.
   * 得到映射器
   * 这个巧妙的使用了泛型,使得类型安全
   * 到了MyBatis 3,还可以用注解,这样xml都不用写了
   * @param <T> the mapper type
   * @param type Mapper interface class
   * @return a mapper bound to this SqlSession
   */
  <T> T getMapper(Class<T> type);

org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper

  @Override
  public <T> T getMapper(Class<T> type) {
    //最后会去调用MapperRegistry.getMapper
    return configuration.<T>getMapper(type, this);
  }

org.apache.ibatis.session.Configuration#getMapper

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

从上面可以看到Configuration中的mapperRegistry,已经保存了UserMapper的Class对象,接下来就是用JDK自带的动态代理生成UserMapper的代理对象
org.apache.ibatis.binding.MapperRegistry#getMapper

  //返回代理类
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      /*利用JDK动态代理返回代理对象*/
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)

  public T newInstance(SqlSession sqlSession) {
    /*创建一个MapperProxy对象,这个对象其实是实现了InvocationHandler接口的一个对象,其中泛型T就是我们的UserMapper*/
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    // 用JDK自带的动态代理实例化并返回代理对象
    return newInstance(mapperProxy);
  }

org.apache.ibatis.binding.MapperProxy

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
/**
 * 映射器代理,代理模式
 *
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
    //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    //这里优化了,去缓存中找MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //执行
    return mapperMethod.execute(sqlSession, args);
  }

  //去缓存中找MapperMethod
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      //找不到才去new
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.binding.MapperProxy)

  protected T newInstance(MapperProxy<T> mapperProxy) {
    //用JDK自带的动态代理生成映射器
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

所以其实就是典型的jdk动态代理
最后返回的UserMapper就是一个JDK动态代理的对象

调用mapper接口的方法时的处理

测试案例的第三行代码

User user = mapper.findUserByUsername("admin");

可以发现,不出意外的进入了invoke方法,就是jdk动态代理的增强方法。
org.apache.ibatis.binding.MapperProxy#invoke

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
    //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    //这里优化了,去缓存中找MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //执行
    return mapperMethod.execute(sqlSession, args);
  }

首先会从缓存中取MapperMethod,但是取不到,所有会new一个MapperMethod
org.apache.ibatis.binding.MapperProxy#cachedMapperMethod

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      //找不到才去new
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      /*存入缓存中*/
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

在这里插入图片描述
Method对象时java.lang.reflect反射包下的对象,代表当前UserMapper的findUserByUsername方法。每一个mapper接口里的方法,都会有一个MapperMethod 与之对应
创建MapperMethod
org.apache.ibatis.binding.MapperMethod#MapperMethod

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    /*实例化SqlCommand对象,保存到成员变量*/
    this.command = new SqlCommand(config, mapperInterface, method);
    /*实例化MethodSignature对象,保存到成员变量*/
    this.method = new MethodSignature(config, method);
  }
  ...
}

会保存两个成员变量SqlCommand 和MethodSignature ,都是MapperMethod 的内部类

MapperMethod的execute方法执行查询
org.apache.ibatis.binding.MapperMethod#execute

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //可以看到执行时就是4种情况,insert|update|delete|select,分别调用SqlSession的4大类方法
    if (SqlCommandType.INSERT == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      if (method.returnsVoid() && method.hasResultHandler()) {
        //如果有结果处理器
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        //如果结果有多条记录
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        //如果结果是map
        result = executeForMap(sqlSession, args);
      } else {
        //否则就是一条记录
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

这里是只查一条记录,所以进入最后一个分支,执行method.convertArgsToSqlCommandParam(args);获取param,其实就是一个字符串,传入的参数"admin",最后执行result = sqlSession.selectOne(command.getName(), param);进行查询操作

接下来要调用sqlSession的selectOne方法,其实sqlSession的这些方法可以在main方法中直接使用,而且这才是最原始的方法,所以main方法完全可以修改成以下这样,这样就直接调用sqlSession的selectOne方法,程序照样能正常执行。

public class _Main {
    public static void main(String[] args) {
        // 通过获取对象当前的类类型获取当前类型的类加载器来加载类类路径下的资源文件以此构建SqlSessionFactory
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(_Main.class.getClassLoader().getResourceAsStream("mybatis-config.xml"));
        // 创建SqlSession实例来自行数据库的所有方法和sql语句
        SqlSession session = sessionFactory.openSession();
        User user = (User)session.selectOne("com.mybatis.mapper.UserMapper.findUserByUsername", "admin");
        System.out.println("user:"+user);
    }
}

org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)

  //核心selectOne
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    //转而去调用selectList,很简单的,如果得到0条则返回null,得到1条则返回1条,得到多条报TooManyResultsException错
    // 特别需要主要的是当没有查询到结果的时候就会返回null。因此一般建议在mapper中编写resultType的时候使用包装类型
    //而不是基本类型,比如推荐使用Integer而不是int。这样就可以避免NPE
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

在这里插入图片描述
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object)

  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)

  //核心selectList
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //根据statement id找到对应的MappedStatement
      MappedStatement ms = configuration.getMappedStatement(statement);
      //转而用执行器来查询结果,注意这里传入的ResultHandler是null
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

在这里插入图片描述
然后就是executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

执行前首先会把参数包装成集合
org.apache.ibatis.session.defaults.DefaultSqlSession#wrapCollection

  //把参数包装成Collection
  private Object wrapCollection(final Object object) {
    if (object instanceof Collection) {
      //参数若是Collection型,做collection标记
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("collection", object);
      if (object instanceof List) {
        //参数若是List型,做list标记
        map.put("list", object);
      }
      return map;
    } else if (object != null && object.getClass().isArray()) {
      //参数若是数组型,,做array标记
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("array", object);
      return map;
    }
    //参数若不是集合型,直接返回原来值
    return object;
  }

但这里不是集合类型,所以直接返回

executor.query进入到的是.CachingExecuto的query方法
org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 通过MappedStatement的getBoundSql获取绑定了查询sql的BoundSql对象
    BoundSql boundSql = ms.getBoundSql(parameterObject);
	//query时传入一个cachekey参数
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    // 执行查询
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

org.apache.ibatis.mapping.MappedStatement#getBoundSql

  public BoundSql getBoundSql(Object parameterObject) {
	//其实就是调用sqlSource.getBoundSql
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    //剩下的可以暂时忽略
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }

在这里插入图片描述
org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    //默认情况下是没有开启缓存的(二级缓存).要开启二级缓存,你需要在你的 SQL 映射文件中添加一行: <cache/>
    //简单的说,就是先查CacheKey,查不到再委托给实际的执行器去查
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    } // delegate是一个SimpleExecutor对象,代理本Executor真正执行查询
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

真正执行查询的是SimpleExecutor对象,他又继承了BaseExecutor,delegate. query方法会进入到BaseExecutor的query方法
org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)

 @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    //如果已经关闭,报错
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    //先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      //加一,这样递归调用到上面的时候就不会再清局部缓存了
      queryStack++;
      //先根据cachekey从localCache去查
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        //若查到localCache缓存,处理localOutputParameterCache
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //从数据库查
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      //清空堆栈
      queryStack--;
    }
    if (queryStack == 0) {
      //延迟加载队列中所有元素
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      //清空延迟加载队列
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
    	//如果是STATEMENT,清本地缓存
        clearLocalCache();
      }
    }
    return list;
  }

这里会进入到从数据库查的那一步
org.apache.ibatis.executor.BaseExecutor#queryFromDatabase

  //从数据库查
  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    //先向缓存中放入占位符???
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
        /*执行查询,返回查询结果*/
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      //最后删除占位符
      localCache.removeObject(key);
    }
    //加入缓存
    localCache.putObject(key, list);
    //如果是存储过程,OUT参数也加入缓存
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

进入doQuery方法
org.apache.ibatis.executor.SimpleExecutor#doQuery

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //新建一个StatementHandler
      //这里看到ResultHandler传入了
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //准备语句
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler.query
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

org.apache.ibatis.executor.statement.RoutingStatementHandler#query

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  	/*PreparedStatementHandler.query*/
    return delegate.<E>query(statement, resultHandler);
  }

调用到PreparedStatementHandler的query,里面就是jdbc的代码,查询然后返回结果
org.apache.ibatis.executor.statement.PreparedStatementHandler#query

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  	/*获取boundSql中的sql字符串*/
    PreparedStatement ps = (PreparedStatement) statement;
    /*执行sql查询*/
    ps.execute();
    // 先执行Statement.execute,然后交给ResultSetHandler.handleResultSets
    // 里面就是结果集的处理,处理完了返回的才是User对象
    return resultSetHandler.<E> handleResultSets(ps);
  }

然后list就是返回的查询结果
在这里插入图片描述

Spring整合Mybatis

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--<context:component-scan base-package="spring.dao"/>-->
    <context:component-scan base-package="service"/>

    <context:component-scan base-package="mapper"/>

    <util:properties id="dbConfig" location="classpath:db.properties" />

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="url" value="#{dbConfig.url}"/>
        <property name="driverClassName" value="#{dbConfig.driver}"/>
        <property name="username" value="#{dbConfig.user}"/>
        <property name="password" value="#{dbConfig.password}"/>
        <property name="initialSize" value="#{dbConfig.initSize}"/>
        <property name="maxActive" value="#{dbConfig.maxActive}"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="mapper"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:mappers/UserMapper.xml"/>
    </bean>

</beans>

SqlSessionFactoryBean创建SqlSessionFactory

因为里面配置了SqlSessionFactoryBean,spring就会把创建并初始化SqlSessionFactoryBean,所以这个类就是分析Spring整合Mybatis的入口 。
org.mybatis.spring.SqlSessionFactoryBean

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
	...

    public void setDataSource(DataSource dataSource) {
        if (dataSource instanceof TransactionAwareDataSourceProxy) {
            this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();
        } else {
            this.dataSource = dataSource;
        }

    }
	...

    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");
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

	...

    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }
	...
}

SqlSessionFactoryBean 实现了两个接口FactoryBean和InitializingBean
实现FactoryBean时:动过getBean从Spring获取到的Bean就不是当前bean对象,而是该对象的getObject返回的对象
实现InitializingBean时:会在Bean创建完成且执行完属性注入后,进行初始化回调时,调用该bean重写的afterPropertiesSet方法

org.mybatis.spring.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");
        // 创建SqlSessionFactory并保存到成员变量
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

spring执行SqlSessionFactoryBean的初始化回调方法afterPropertiesSet时,就会去实例化SqlSessionFactory
然后每次getBean时,返回的都是SqlSessionFactory对象

    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }
		// 返回SqlSessionFactory 
        return this.sqlSessionFactory;
    }

Mapper的创建和管理

我们都知道在Spring整合了Mybatis后,所有的Mapper都是通过自动注入的方式获取,无需再像原生的Mybatis那要通过sqlSession获取,也就是说我们通过context.getBean(UserMapper.class)的方式就可以获取。因此所有的Mapper接口的代理对象肯定都初始化并保存到Spring的单例缓存池中。

在配置文件中有一项配置

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="mapper"/>
    </bean>

Mapper的创建的分析就从MapperScannerConfigurer这个类开始
org.mybatis.spring.mapper.MapperScannerConfigurer

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
	...
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
            this.processPropertyPlaceHolders();
        }

        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.registerFilters();
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }
    ...
}

MapperScannerConfigurer 实现了BeanDefinitionRegistryPostProcessor,并且重写了该接口的方法postProcessBeanDefinitionRegistry

这个方法会在Spring执行refresh方法时,创建了BeanFactory,扫描了xml配置的所有BeanDefinition后,进行invokeBeanFactoryPostProcessors方法时被调用
org.springframework.context.support.AbstractApplicationContext#refresh

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			...

			/**
			 * 创建DefaultListableBeanFactory,
			 * 扫描并注册BeanDefinition到BeanDefinitionMap中,
			 * 返回这个BeanFactory
			 * 注意:
			 * 	如果是注解的bean,不会在这里注册BeanDefinition到BeanDefinitionMap
			 * 	而是在下面的invokeBeanFactoryPostProcessors里,通过bean工厂后置处理器完成
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			...

			try {
				...
				
				// 调用上下文中注册为bean的工厂处理器。
				invokeBeanFactoryPostProcessors(beanFactory);

				...
			}

			catch (BeansException ex) {
				...
			}

			finally {
				...
			}
		}
	}

org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		/**
		 * 调用容器中所有实习了BeanDefinitionRegistryPostProcessor的实现类的postProcessBeanDefinitionRegistry方法
		 * 而BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor
		 * 其中Spring-Mybatis就是利用这个方法进行Mapper的扫描和注册成BeanDefinition
		 */
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

		// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
		// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
		if (!IN_NATIVE_IMAGE && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}

org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
			...
			/**
			 * 循环遍历所有的BeanFactoryPostProcessor,
			 * 发现是BeanDefinitionRegistryPostProcessor类型,
			 * 则调用他的postProcessBeanDefinitionRegistry.
			 * 把BeanDefinitionRegistry对象传入进去,
			 * 所以这里允许BeanDefinitionRegistryPostProcessor可以继续像容器注册一些额外的BeanDefinition
			 * 例如Spring-Mybatis中注册了所有扫描到的Mapper
			 */
			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					registryProcessors.add(registryProcessor);
				}
				else {
					regularPostProcessors.add(postProcessor);
				}
			}
			...
}

以上就是MapperScannerConfigurer 的postProcessBeanDefinitionRegistry方法,在Spring当中被调用到的时机
然后回到MapperScannerConfigurer 的postProcessBeanDefinitionRegistry方法中

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
            this.processPropertyPlaceHolders();
        }
		// registry封装成ClassPathMapperScanner 对象,而BeanDefinitionRegistry 有扫描注册BeanDefinition的能力
        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.registerFilters();
        // 扫描配置文件中MapperScannerConfigurer 配置的basePackage对应的路径,加载所有Mapper成BeanDefinition,注册到Spring容器中(一个名叫beanDefinitionMaps的Map)
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }

org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan

    public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        // 扫描配置文件中basePackage的指定路径
        this.doScan(basePackages);
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }

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

org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan

    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        // 要返回的包装了BeanDefinition的BeanDefinitionHolder对象集合
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
        // 扫描路径字符串数组,这里就只有一个mapper路径
        String[] var3 = basePackages;
        int var4 = basePackages.length;
		// 遍历所有的扫描路径
        for(int var5 = 0; var5 < var4; ++var5) {
            String basePackage = var3[var5];
            //扫描指定路径,加载成BeanDefinition,返回一个Set<BeanDefinition>集合
            Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
            // 获取BeanDefinition集合的迭代器
            Iterator var8 = candidates.iterator();
			// 遍历所有的BeanDefinition
            while(var8.hasNext()) {
                BeanDefinition candidate = (BeanDefinition)var8.next();
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
                }

                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
                }

                if (this.checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    // 把BeanDefinition注册到Spring容器中
                    this.registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }

        return beanDefinitions;
    }

然后这些个BeanDefinition所指定的Class类型是MapperFactoryBean,他有一个泛型Class mapperInterface,代表了当前Mapper接口的类型。然后MapperFactoryBean又实现了FactoryBean接口,当调用spring的getBean方法获取这个bean是,返回的就是Mapper的代理对象
org.mybatis.spring.mapper.MapperFactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
	private Class<T> mapperInterface;
	...
	public T getObject() throws Exception {
		// sqlSession.getMapper(UserMapper.class)
        return this.getSqlSession().getMapper(this.mapperInterface);
    }
	...
}

可以看到在getObject中就调用了sqlSession获取Mapper的方法,然后这个Mapper的代理类自然就可以被Spring当做一个Bean来管理了,所以我们用到时,就可以通过自动注入的方式获取。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值