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)。

 

發佈了4 篇原創文章 · 獲贊 1 · 訪問量 2818
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章