美文网首页
Mybatis源码分析1-如何获取Mapper实现类

Mybatis源码分析1-如何获取Mapper实现类

作者: 6cc89d7ec09f | 来源:发表于2018-12-15 15:26 被阅读17次

    入口:一般会在DAO层配置中,比如下面是我的配置

    <!-- 数据库连接池 -->
        <!-- 加载配置文件 -->
        <context:property-placeholder location="classpath*:properties/*.properties" />
        <!-- 数据库连接池 -->
        <bean id="dataSource" name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
            destroy-method="close">
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            <property name="driverClassName" value="${jdbc.driver}" />
            <property name="maxActive" value="10" />
            <property name="minIdle" value="5" />
        </bean>
        
        <!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 数据库连接池 -->
            <property name="dataSource" ref="dataSource" />
            <!-- 加载mybatis的全局配置文件 -->
            <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
        </bean>
        <!--包扫描类-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.pinyougou.mapper" />
        </bean>
    

    在这里面,我们配置了id为sqlSessionFactory的bean。那么这也是我们的入口

    1 SqlSessionFactoryBean

    • SqlSessionFactoryBean实现了FactoryBean,那么最终Spring返回的应该是getObject方法的返回值,
    SqlSessionFactoryBean.getObject
      ->afterPropertiesSet()
        ->buildSqlSessionFactory
          ->this.sqlSessionFactoryBuilder.build(configuration)
            ->SqlSessionFactoryBuilder.build(Configuration config)
              ->new DefaultSqlSessionFactory(config);
    

    由此可以看出,id为sqlSessionFactory的bean默认是DefaultSqlSessionFactory。Mybatis的官方推荐,SqlSessionFactory最好是做成单例的,不需要频繁创建。

    2 获取session

    通过api获取session的方式

    SqlSession session = sqlSessionFactory.openSession();
    try {
      BlogMapper mapper = session.getMapper(BlogMapper.class);
      Blog blog = mapper.selectBlog(101);
    } finally {
      session.close();
    }
    

    那么我们就看DefaultSqlSessionFactory.openSession方法,如下

    public class DefaultSqlSessionFactory implements SqlSessionFactory{
      public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    
      }
      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          //创建事务管理器
          Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType);
            //返回一个DefaultSqlSession
          return new DefaultSqlSession(configuration, executor, autoCommit);
      }
    }
    

    再看DefaultSqlSession.getMapper方法

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

    这个configuration对象是在哪里创建,又是哪里传入的呢?其实是从一开始的SqlSessionFactoryBean中创建好后,赋值给SqlSessionFactoryBuilder的build的方法参数,一路传到了DefaultSqlSession中。也就是说configuration是隶属于DefaultSqlSessionFactory的,生命周期很长。
    configuration究竟有什么呢?

    public class Configuration {
    //Mapper 接口一开始被添加到在SqlSessionFactory中的名为 MapperRegistry 类的 
    //HashMap中, key = Mapper class value = 创建当前Mapper的工厂。
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);
    
    ...
    

    原来,所有的Mapper接口被扫描后,都会被存储在Configuration的mapperRegistry里的Map中。而且key = Mapper class value = 创建当前Mapper的工厂


    接着看configuration.getMapper(..)方法

    //存储Mapper接口和创建Mapper的工厂
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
     public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
         return mapperProxyFactory.newInstance(sqlSession);
      }
    

    那我们知道了knownMappers.get(type) 返回的是一个创建Mapper的工厂MapperProxyFactory
    接下来我们看看 MapperProxyFactory是如何创建的。直接看MapperProxyFactorynewInstance(SqlSession sqlSession)方法

    public class MapperProxyFactory<T> {
      @SuppressWarnings("unchecked")
      protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }
    
      public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
      }
    }
    

    可以清楚的看到,通过jdk的动态代理创建了代理类。那么我们需要关注一下,代理类的InvocationHandler做了哪些事情

    public class MapperProxy<T> implements InvocationHandler, Serializable {
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         //先获取缓存起来的MapperMethod 对象。这个Method对象对应的就是我们在Mapper接口里写的方法
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        //这里才是执行数据库操作的地方
        return mapperMethod.execute(sqlSession, args);
      }
    }
    
    private MapperMethod cachedMapperMethod(Method method) {
        //这里需要注意methodCache这个Map,不是保存在本类里的。
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
          mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
          methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
      }
    //构造
    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
      }
    

    这里需要注意methodCache这个Map,不是保存在本类里的。看MapperProxy的有参构造传进来的。那么谁创建的MapperProxy呢?上面我们说了是Mapper对应的Mapper工厂MapperProxyFactory。这个对象也是保存在Configuration中,生命周期很长。

    这就解释了,为什么 官网上推荐,让Mapper的实现类 用后就可以舍弃(做局部变量),但是这样每次都去创建难道不耗时,答案是不耗时,因为核心的MapperMethod都已经被缓存起来了。

    相关文章

      网友评论

          本文标题:Mybatis源码分析1-如何获取Mapper实现类

          本文链接:https://www.haomeiwen.com/subject/mwhahqtx.html