Mybatis源码(2)-mapper创建过程

问题:项目中一般会声明Mapper接口,接口包含了访问db的相关方法,然后在对应的xml文件中配置和接口暴露方法相对应的sql,我们没有实现Mapper接口,为什么可以通过如下代码进行访问数据库的操作?

    public void testGetUserByUserName() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("config/mybatis/mybatis-config.xml");
        SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        System.out.println(mapper.getUserByUserName("test"));
    }

上一章节介绍了mybatis标签解析的内容,构建了SqlSessionFactory,本章节分析mapper的创建过程。首先通过sqlSessionFactory.openSession()获取session:

DefaultSqlSessionFactory:

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

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 获取环境对象
      final Environment environment = configuration.getEnvironment();
      // 默认的TransactionFactory是ManagedTransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 默认的tranction对象(jdbc connection的包装对象,管理connection的creation、preparation、commit/rollback、close等)是ManagedTransaction。
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 构建executor对象
      final Executor executor = configuration.newExecutor(tx, execType);
      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();
    }
  }

Configuration:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      // 批处理executor(对select操作无效)
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      // 重用statement的执行器(在内存中缓存生成的statement对象,提高效率)
      executor = new ReuseExecutor(this, transaction);
    } else {
      // 普通执行器(^_^)
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      // 支持缓存的executor(包装上述executor),配置cacheEnabled属性时生效
      executor = new CachingExecutor(executor);
    }
    // 如果配置了插件(实现Interceptor接口),用插件封装executor
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

InterceptorChain:

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

Interceptor:

default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public static Object wrap(Object target, Interceptor interceptor) {
    // 获取interceptor配置的classType,以及拦截方法的映射
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    // 获取type所有在signatureMap中的interface
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap)); // jdk动态代理生成代理。
    }
    return target;
  }

Plugin: 实现了InvocationHandler接口

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 判断interceptor是否配置了当前class、method的插件。
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args)); // 调用插件方法
      }
      return method.invoke(target, args); // 直接通过反射执行目标method
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

至此,通过动态代理的方式将插件织入到executor中,并生成了DefaultSqlSession。

下一步是调用SqlSession.getMapper()获取目标mapper。

DefaultSqlSession:

  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

Configuration:

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

MapperRegistry: 

 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 根据类型,从knownMappers中获取mapperProxyFactory
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession); // 构建mapper的代理对象
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

MapperProxyFactory:

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

  protected T newInstance(MapperProxy<T> mapperProxy) {
    // 通过动态代理的方式创建代理对象,mapperProxy实现了InvocationHandler。
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

至此,mapper创建流程介绍完成,下一步将分析sql的执行流程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatisMapper 模块主要由两部分组成: 1. Mapper 接口:Mapper 接口是一个 Java 接口,其中定义了各种 SQL 操作的方法,这些方法都有对应的 SQL 语句。 2. Mapper XML 文件:Mapper XML 文件是一个独立的 XML 文件,其中定义了与 Mapper 接口中方法对应的 SQL 语句以及参数的映射关系。 MyBatis 在解析 Mapper 接口和 Mapper XML 文件时,会通过 Java 动态代理技术动态生成 Mapper 接口的实现类,同时会将 Mapper XML 文件中定义的 SQL 语句解析成相应的 SQL 语句对象并存放在内存中,方便后续的操作。 Mapper 接口的源码: ```java public interface UserMapper { // 根据 ID 查询用户 @Select("SELECT * FROM user WHERE id = #{id}") User getUserById(Integer id); // 添加用户 @Insert("INSERT INTO user(username,password) VALUES(#{username},#{password})") int addUser(User user); // 更新用户信息 @Update("UPDATE user SET username = #{username},password = #{password} WHERE id = #{id}") int updateUser(User user); // 根据 ID 删除用户 @Delete("DELETE FROM user WHERE id = #{id}") int deleteUser(Integer id); } ``` Mapper XML 文件的源码: ```xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.mapper.UserMapper"> <!-- 根据 ID 查询用户 --> <select id="getUserById" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> <!-- 添加用户 --> <insert id="addUser" parameterType="com.example.entity.User"> INSERT INTO user(username,password) VALUES(#{username},#{password}) </insert> <!-- 更新用户信息 --> <update id="updateUser" parameterType="com.example.entity.User"> UPDATE user SET username = #{username},password = #{password} WHERE id = #{id} </update> <!-- 根据 ID 删除用户 --> <delete id="deleteUser" parameterType="java.lang.Integer"> DELETE FROM user WHERE id = #{id} </delete> </mapper> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值