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

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

ini 复制代码
    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:

java 复制代码
  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:

ini 复制代码
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:

typescript 复制代码
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

Interceptor:

javascript 复制代码
default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
typescript 复制代码
  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接口

typescript 复制代码
  @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:

typescript 复制代码
  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

Configuration:

typescript 复制代码
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

MapperRegistry:

typescript 复制代码
 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:

typescript 复制代码
  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的执行流程。

相关推荐
Jabes.yang16 小时前
互联网大厂Java面试:从Spring到Kafka的技术挑战
spring boot·spring cloud·eureka·kafka·mybatis·jpa·java面试
tuokuac18 小时前
MyBatis“别名扫描”功能
java·mybatis
lunzi_fly1 天前
【源码解读之 Mybatis】【基础篇】-- 第3篇:SqlSession的创建与生命周期
mybatis
对不起初见i2 天前
MyBatis-Plus 全方位深度指南:从入门到精通
java·数据库·mybatis
lunzi_fly2 天前
【源码解读之 Mybatis】【基础篇】--第4篇:Mapper 接口的动态代理机制解析
mybatis
lunz_fly19922 天前
【源码解读之 Mybatis】【基础篇】--第4篇:Mapper 接口的动态代理机制解析
mybatis
小信丶2 天前
Spring Boot启动报错:Failed to configure a DataSource 全面解析与解决方案
spring boot·后端·mybatis
会功夫的李白2 天前
捕获Mybatis执行的Sql
sql·mybatis
小小小LIN子7 天前
mybatis升级到mybatis plus后报Parameter not found
mybatis