Mybatis源码日知录

         MyBatis源码相对于Spring应该是层次比较清晰,容易理解的;其实简单来说就是解耦、封装,让开发者更关注业务层的开发,实现简单而又方便的调用。

本文主要介绍mybatis框架的几个重要点:xml文件加载初始化、mapper接口动态代理加强和源码的一些模块以及所涉及的设计模式;通过阅读本文之后你将会学到Mybatis的工作流程以及常用的框架技术和模式。

文章目录

一 、xml文件加载初始化

二、Mapper接口动态代理加强

三、Mybatis涉及的模块以及设计模式使用


注:以下介绍主要是基于使用注解的SpringBoot配置。

一 、xml文件加载初始化

Mybatis加载XML文件核心类:

XMLConfigBuilder:解析Mybatis-config.xml文件节点</configuration>;这一块可以忽略,因为现在几乎都是Spring集成Mybatis,通过application.properties来配置,跳过config文件解析直接进入第二阶段XMLMapperBuilder解析mapper.xml文件。

XMLMapperBuilder:解析mapper.xml文件节点</mapper>;Mybatis结合springBoot或者Spring使用时,在配置dataSource创建SqlSessionFactory时启动的XMLMapperBuilder对象。主要有ResultMap、Sql节点等。

XMLStatementBuilder:解析select|insert|update|delete,封装成MappedStatement。

bing绑定:绑定mapper class和对代理工厂对象,mapperRegistry是其注册中心;创建Mapper接口的代理工厂类MapperProxyFactory,该类主要为Mapper接口方法生成代理类(MapperProxy(JDK动态代理))进行增强。

集成SpringBoot初始化XML:

 配置数据源时SqlSessionFactory是必不可少,它是生成SqlSession的工厂类(默认DefaultSqlSessionFactory);而SqlSessionFactory是由SqlSessionFactoryBean实例化的如下图代码:

  • MapperLocations 就是设置Mapper文件资源的;
  • plugins是引入插件例如分页插件,关于插件第三节中详细介绍;
  • 加载解析xml,在getObject()------->afterPropertiesSet()------->buildSqlSessionFactory(),最后这个方法会真正调用Mybatis加载XML文件核心类加载、初始化mapper文件,这边主要介绍Mapper是如何绑定代理工厂的

注意BeanFactory和FactoryBean的区别

public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        // 设置mybatis的xml所在位置
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/test1/*.xml"));
        bean.setPlugins(new Interceptor[]{DefinePageInterceptor.buildPageInterceptor()});
        // FactoryBean就是通过getObject()获取真正的bean实例(注意BeanFactory和FactoryBean的区别)
        return bean.getObject();
}
Mapper是 如何绑定代理工厂(注意文件中的注释):
// 1、XMLMapperBuilder 为Mapper绑定代理工厂
private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          // 1. 绑定Mapper的代理工厂MapperProxyTactory---入口
          configuration.addMapper(boundType);
        }
      }
    }
}

-----------------------------------------------------------------------------
// 1、MapperRegistry 注册Mapper对应的代理工厂类
public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

二、Mapper接口动态代理加强

问题思考:Mybatis是如何通过Mapper接口进行sql操作的?

MapperProxy就是Mapper接口的代理类,这个代理类由代理工厂类MapperProxyFactory生成如图所示(此工厂类在第一步中已经将Mapper接口类型作为key进行缓存),MapperProxy的必须属性参数有mapper相关的sqlsession,mapper接口类型Class。 

// 1、MapperRegistry:注册中心获取代理工厂  
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 {
      // 2、代理工厂创建代理类
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

 -------------------------------------------------------------------------------------

 // 1、MapperProxyFactory 代理工厂类创建代理
 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);
  }

1、动态代理当调用Mapper接口中的方法就会交由MapperProxy进行代理

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 1、获取MapperMethod 执行execute(method的执行是放在MapperMethod中的)
    final MapperMethod mapperMethod = cachedMapperMethod(method); 
    //  2、MapperMethod中根据SqlCommand的类调用sqlSession的 insert|update.....
    return mapperMethod.execute(sqlSession, args);
  }


// 3、先取缓存如果没有,创建一个新的(封装了mapper接口、要执行的方法以及贯穿整个mybatis的Configuration)
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

method.execute(sqlsession,args)真正 调用sqlSession的地方。走进去看一下sqlSession触发不同的sql类型,但是对于select由于返回类型可能不同,再继续处理。                      

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 根据sqlCommand类型执行insert|update
    switch (command.getType()) {
      case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        // 2、根据select返回类型的不同执行不同的方法,,其最终都会执行selectList,只是前期准备不同。
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        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;
  }

2、Executor执行:其实最终还是要使用Executor执行的           

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 1、根据namespace获取MappedStatement
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 2、wrapCollection()封装参数值
      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有两个实现类,类图如下;Executor是由Configuration创建的,并且使用了装饰器模式以及动态代理进行加强(interceptorChain.pluginAll(executor))

// 1、DefaultSqlSessionFactory 中创建DefaultSqlSession
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);
       // 2、DefaultSqlSessionFactory中创建DefaultSqlSession
      final Executor executor = configuration.newExecutor(tx, execType);
      // 3、封装参数到SqlSession中
      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();
    }
  }
------------------------------------------------------------------------------------
// 1、Configuration中创建Executor 
public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  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 = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    // 2、装饰器模式对基础的Executor进行装饰
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
   // 3、遍历所有的拦截器,然后调用各个拦截器的plugin方法,然后使用Plugin.wrap()为executor生成 
       Plugin代理对象(封装了interceptor、待拦截的method、target对象)。
   // 拦截器链InterceptorChain会对每一个拦截器依次封装在代理对象中(即:realObject --> 
     proxy1RealObject --> prox2Prox1RealObject)只要符合拦截条件都会被依次代理增强。拦截执行 
     的时候会倒序依次执行(prox2Prox1RealObject-->proxy1RealObject -->realObject)
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

                 

ReuseExecutor:对Statement进行了缓存,重复利用。

                             

 

以SimpleExecutor为例看其处理过程:

下图中doQuery()方法第四行代码configuration.newStatementHandler():在Configuration类中通过RoutingStatementHandler(静态代理)根据MappedStatement的StatementType(默认PREPARED)生成哪一种StatementHandler(类图如下),

 @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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
       // 1、sql执行---最终会使用PreparedStatementHandler (如下代码) 
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 是否对获取的连接进行log加强
    Connection connection = getConnection(statementLog);
   // 获取Statement ----最终会调用instantiateStatement()如下代码
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 参数设值 --- 如下代码  
    handler.parameterize(stmt);
    return stmt;
  }

-----------------------------------------------------------------------------------------
// 1、PreparedStatementHandler 使用Statement执行execute() (和使用jdbc一模一样的)
 @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    // 2、结果集处理见(ResultSetHandler处理小节)
    return resultSetHandler.<E> handleResultSets(ps);
  }

// 3、PreparedStatementHandler 使用connection获取Statement (和使用jdbc一模一样的)
  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }
  // 4、参数设值(获取ParameterMapping使用MetaObject(反射模块详细介绍)取值,然后占位符位置设值)
  @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

StatementHandler继承关系:

                         

3、ResultSetHandler解析查询结果:结果解析(ResultSetHandler)唯一默认实现类DefaultResultSetHandler,也会被加强;

// 1、  Configuration中ResultSetHandler生成 (DefaultResultSetHandler默认唯一实现)
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

----------------------------------------------------------------------------------------
// 2、DefaultResultSetHandler处理查询结果接上一小节
  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    // 返回结果集处理封装对象。-----不详细介绍
    while (rsw != null && resultMapCount > resultSetCount) {
       // 3、获得resultMap,实体类和表中数据字段的对应关系
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 4、将值设置成对应的resultmap对象
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    ........

    return collapseSingleResultList(multipleResults);
  }

总结:ResultSetHandler、StatementHandler职责分工明确、层次分明,jdbc操作数据库的整套代码被完全封装起来,其本质和逻辑还是一样的。

三、Mybatis涉及的模块以及设计模式使用

  1. 反射模块:查的结果ResultSet通过反射构建bean。(实例化目标对象和对象属性赋值需要反射生成)---工厂模式

反射的核心类:

      1)Reflector:存放class定义的属性、方法信息

      2)DefaultReflectorFactory:依据class类新创建Reflector并缓存

      3)MetaClass:引用ReflectorFactory、Reflector,可以获取类的元信息

      4)ObjectWrapper:对bean、MetaClass的封装,可以实现对bean属性的设值以及获取

      5)DefaultObjectFactory:实例化对象

      6)MetaObject:主要封装了beanObjectWrapper、DefaultObjectFactory、DefaultReflectorFactory,因此可以通过MetaObject设置和获取bean的属性。(主要是通过ObjectWrapper操作)

  • 用于实例化目标对象的类

------ObjectFactory:MyBatis每次创建结果对象的新实例时,使用ObjectFactory构建POJO,默认实现DefaultObjectFactory(反射实例化对象)

 

 

反射创建对象(注意区别):

 

  • 用于对象属性赋值的类

------ReflectorFactory:创建Reflector的工厂类,Reflector是MyBatis反射模块的基础,每个Reflector对象都对应一个类,在其中缓存了反射操作所需要的类元信息。默认实现类DefaultReflectorFactory(读取类元信息封装在Reflector中,每一个类都有一个Reflector)。

 

Reflector(核心元信息缓存类):

   

------MetaObject:

MetaObject封装了对象元信息,包装了MyBatis中的五个核心的反射类,也是提供给外部使用的反射工具类,可以利用它读取或者修改对象的属性信息(其实也是通过ObjectWrapper调用相关的方法,例如BeanWrapper)。      

// MetaObject 包装五个核心类 
  private final Object originalObject;
  private final ObjectWrapper objectWrapper;
  private final ObjectFactory objectFactory;
  private final ObjectWrapperFactory objectWrapperFactory;
  private final ReflectorFactory reflectorFactory;

BeanWrapper作用如下:(是对bean的封装可以给bean属性设置值,也可以获取某个属性的值)

public class BeanWrapper extends BaseWrapper {

  //1、BeanWrapper 封装了bean对象以及对应的类元信息,通过BeanWrapper get()和set()获取属性值或者设置属性值

  private final Object object;
  private final MetaClass metaClass;

  public BeanWrapper(MetaObject metaObject, Object object) {
    super(metaObject);
    this.object = object;
    this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
  }
}

MetaClass(封装类元信息,并且也是通过Reflector来获取类的相关属性信息):

public class MetaClass {

// 1、封装 类的属性和方法信息,并且可以通过此类获取属性等信息(如方法返回类型,参数类型)
  private final ReflectorFactory reflectorFactory;
  private final Reflector reflector;

  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;
    this.reflector = reflectorFactory.findForClass(type);
  }

  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }
}

 总结:反射模块核心类使用如下

 public void mybatisReflect(){

        DefaultObjectFactory defaultObjectFactory = new DefaultObjectFactory();
        ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
        // 1、DefaultObjectFactory创建bean
        User user = defaultObjectFactory.create(User.class);

        DefaultReflectorFactory defaultReflectorFactory = new DefaultReflectorFactory();
        // 2、创建MetaObject对象并在BeanWrapper中使用defaultReflectorFactory创建Reflector(Reflector加载了类元信息)
        MetaObject metaObject = MetaObject.forObject(user, defaultObjectFactory, objectWrapperFactory, defaultReflectorFactory);
        // 3、BeanWrapper
        ObjectWrapper objectWrapper = metaObject.getObjectWrapper();
        // 4、使用ObjectWrapper读取对象信息,并对对象属性进行赋值操作(最终调用Reflector获取method,然后通过反射设值)
        objectWrapper.set(new PropertyTokenizer("userName"),"xiaodai");
        String userName = user.getUserName();
        System.out.println(userName);

    }

2. 插件模块:

MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

    -------执行器的拦截-----------------------------------

  • ParameterHandler (getParameterObject, setParameters)

    -------参数处理的拦截--------------------------------

  • ResultSetHandler (handleResultSets, handleOutputParameters)

    --------结果集处理的拦截----------------------------

  • StatementHandler (prepare, parameterize, batch, update, query)

    --------sql语法构建的拦截--------------------------

// 通过pluginAll实现对四大接口拦截设置  
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//RoutingStatementHandler 其实是个静态代理,根据不同要求创建不同的对象
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

  public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

这4个方法实例化了对应的对象之后,都会调用interceptorChain的pluginAll方法,InterceptorChain的pluginAll就是遍历所有的拦截器,然后调用各个拦截器的plugin方法。注意:拦截器的plugin方法的返回值会直接被赋值给原先的对象。

在创建SqlSession的时候最先会执行的Configuration的newExecutor()并进行拦截器配置和加强。例:加入PageInterceptor拦截器,newExecutor()时会调用interceptorChain的pluginAll方法,然后在执行PageInterceptor.plugin(),最后会使用Plugin.wrap()为executor生成Plugin代理对象。最后在执行executor.query()时会被Plugin加强,根据PageInterceptor注解@Signature判断是否是要拦截的方法,PageInterceptor拦截主要是分页处理。

        Plugin这个类的作用就是根据 @Interceptors注解,得到这个注解的属性 @Signature数组,然后根据每个 @Signature注解的type,method,args属性使用反射找到对应的Method。最终根据调用的target对象实现的接口(会和@Signature的type进行比较决定是否返回一个代理对象替代原先的target对象

注:拦截的入口其实就是在代理类Plugin的invoke是真实对象之前根据判断@Signature解析的Method和classType是否和代理的对象的类型和方法一致来决定是否执行拦截器。

        拦截器链InterceptorChain会对每一个拦截器依次封装在代理对象中(即:realObject --> proxy1RealObject --> prox2Prox1RealObject)只要符合拦截条件都会被依次代理增强。拦截执行的时候会倒序依次执行(prox2Prox1RealObject-->proxy1RealObject -->realObject)。

3. Logging模块可以引入第三方例如Slfj----------适配器模式: 

  

mybatis只有属于自己一套的log接口,并没有所有本身的日志,那么如何和市面上面 流行的日志框架做整合呢?这个时候就需要用到适配器模式。

 

如上图实现mybatis的日志实现类都需要实现这个接口。这使用到LogFactory

==org.apache.ibatis.logging.LogFactory== 日志工厂

 

 

如上图 tryImplementation()先判断logConstructor是否为空,只有logConstructor为空的时候tryImplementation里面的方法才会执行。

例如:useSlf4jLogging()  ---> 会获取Slf4jImpl的构造器,当使用LogFactory.getLogger()就会实例化,此时logConstructor已经有值,那么其它的try都不会在创建构造器。

4. 缓存模块--------Cache接口(装饰器模式)

实现类-->PerpetualCache:Cache的基本实现类

               BlockingCache:它是个阻塞版本的缓存装饰器,保证只有一个线程到数据库中查找 key 对应的数据,使用                                                                    ConcurrentHashMap<Object, ReentrantLock> 实现了阻塞功能。

               FifoCache:FifoCache 底层维护了一个队列,当需要清除时会清除队列头部的缓存

               LruCache:LruCache底层维护了一个 LinkedHashMap 它是一种有序的 HashMap。当我们获取一个对象的时候会把                                      这个对象移动到 LinkedHashMap 的尾部表示最近访问的,需要清除的时候清除头部即可。

              SoftCache&WeakCache:是 JVM 中定义的引用类型,是为了更方便的进行JVM垃圾回收的,针对那些只用一次的对象                                   进 行更高效率的回收。

MyBatis中涉及到动态SQL需要通过CacheKey来封装缓存的key值。

构成CacheKey对象的要素(如下图代码):

  • mappedStatement的id
  • 指定查询结果集的范围(分页信息)
  • 查询所使用的SQL语句
  • 用户传递给SQL语句的实际参数值

 BaseExecutor的query:

 

cacheKey比较是否相等判断逻辑:

 

 

总结:MyBatis 的缓存接口 Cache 的基础实现 PerpetualCache 维护了一份 HashMap 作为缓存的实现。同时它还有很多装饰类,比如阻塞式缓存BlockingCache 内部维护了带对象同步器的 ConcurrentHashMap、带清除策略的 FifoCache/LruCache,FifoCache 内部维护了一个队列Dqueue,LruCache内部维护了一份 LinkedHashMap 。这些都使得缓存模块的功能很多,业务方可以根据自己的需求进行灵活组装。

一级缓存和二级缓存:

MyBatis 中的缓存就是说 MyBatis 在执行一次SQL查询或者SQL更新之后,这条SQL语句并不会消失,而是被MyBatis 缓存起来,当再次执行相同SQL语句的时候,就会直接从缓存中进行提取,而不是再次执行SQL命令。

一级缓存:

mybatis的一级缓存是基于sqlsession的缓存,默认开启,不同sqlsession之间的缓存数据区域是不会相互影响的,如果会话失效,则缓存失效。

清除缓存:

当commit或者rollback的时候会清除缓存,并且当执行insert、update、delete的时候也会清除缓存。

mybatis一级缓存失效的四种情况

  1. sqlsession变了 缓存失效
  2. sqlsession不变,查询条件不同,一级缓存失效
  3. sqlsession不变,中间发生了增删改操作,一级缓存失败
  4. sqlsession不变,手动清除缓存,一级缓存失败

二级缓存:

二级缓存是手动开启的,作用域为mapper级别(也可以说MapperStatement级缓存,也就是一个namespace就会有一个缓存),不同的sqlsession两次执行相同的namespace下的sql,且向sqlq中传递的参数也相同,即最终执行相同的sql,则第一次中会从数据库查并存缓存,第二次会从内存查;因为二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的,也就是要求实现Serializable接口,如果存储在内存中的话,实测不序列化也可以的。

如果开启了二级缓存的话,你的Executor将会被装饰成CachingExecutor,缓存是通过CachingExecutor来操作的,查询出来的结果会存在statement中的cache中,若有更新,删除类的操作默认就会清空该MapperStatement的cache(也可以通过修改xml中的属性,让它不执行),不会影响其他的MapperStatement。

  

5、建造者模式:在解析mapper.xml时会通过MapperBuilderAssistant助理进行每一阶段的属性值的builder(例如addResultMap、addMappedStatement)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值