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的执行流程。

​

相关推荐
大漠孤烟s5 天前
4.Mybatis学习-动态sql、缓存、逆向工程
mybatis
毕业设计7036 天前
(免费领源码) SpringBoot 游戏交易平台17600-java、PHP、python、C#、小程序、大数据、单片机、网络工程等)
java·spring boot·mysql·决策树·mybatis·idea·推荐算法
程序员JerrySUN7 天前
Jetson Edge AI 实战01:Nano、Xavier、Orin 怎么选?Jetson 硬件选型详解【视频讲解】
java·数据库·redis·安全·mybatis
linmengmeng_13147 天前
【总结】MyBatis-Plus批量插入与清表的正确姿势
java·spring boot·mysql·mybatis
vx_BS813307 天前
【项目编号:project51381】Spring Boot 婚纱摄影管理系统:套餐预约、在线咨询、支付与客片展示的一站式业务实现
java·spring boot·eclipse·tomcat·mybatis
jaysee-sjc8 天前
【苍穹外卖】Day01:从零认识企业级项目开发
java·开发语言·数据库·mysql·spring·intellij-idea·mybatis
程序猿乐锅8 天前
【黑马点评 | 第二篇】Redis 缓存更新策略与商铺缓存实现
java·网络·redis·spring·mybatis
萧瑟余晖8 天前
MyBatis 核心原理详解
架构·mybatis
MZA6669 天前
实战:基于 XXL-JOB + 策略模式 + Nacos 配置化的 CSV 导出任务系统设计
java·spring boot·mybatis
干到60岁退休的码农11 天前
15.创建获取用户接口与JWT认证器
java·spring boot·mybatis