mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理

mybatis 核心对象

从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个:

Configuration 初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如,插件拦截器,mapper方法元数据,映射器,ObjectFactory和typeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中
SqlSessionFactory  SqlSession工厂
SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler   封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
ParameterHandler   负责对用户传递的参数转换成JDBC Statement 所需要的参数,
ResultSetHandler    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
TypeHandler          负责java数据类型和jdbc数据类型之间的映射和转换
MappedStatement   MappedStatement维护了一条节点的封装, 
SqlSource            负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql 表示动态生成的SQL语句以及相应的参数信息

 

 

关于Connection,SqlSession,SqlSessionFactoty,Transaction,datasource

datasource 相当于Connection连接或者连接池(第三方连接池),SqlSession(mybatis的接口)是封装了mybatis增删改查的方法,SqlSessionFactory 用于创建SqlSession,Transaction是用什么事务管理。

SqlSession 是一次会话,比如一次事务的提交。而一个 connection可以进行多次session会话。 

SqlSession 接口的默认实现类DrfaultSqlSession

在SqlSession执行操作时会从连接池中获取connection

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第1张图片

SessionFactory中打开一个SqlSession

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第2张图片

原生配置

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第3张图片

spring配置

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第4张图片

整体流程

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第5张图片

或者下图

代码演示

1、主程序(我称之为主操作程序)

主程序的主要任务是创建一个sqlsession:

(1)声明一个值为总配置文件的

String String s="conf.xml";

(2)利用

inputStream=Resources.getResourceAsStream(s);

创建一个InputStream,这是一个通向主配置文件的字节流

(3)创建一个sqlsessionfactory,SqlSession,

SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

,这个sqlsession工厂的任务就是打开一个sqlsession

SqlSession sqlSession=sqlSessionFactory.openSession();

(4)利用sqlsession得到接口的对象(动态代理,里面会封装好一个SqlSession):

GetstudentInfo getstudentInfo = sqlSession.getMapper(GetstudentInfo.class);

(这种类型的方法都是通过反射来实现的,即你传了一个对象的class过去,就可以通过反射方式来生成这个对象)

(5)得到所需要的bean对象

 Student student = getstudentInfo.getStudent(1);

   如果没有接口的话,可以使用

Student student=sqlSession.selectOne("shenzhigongsi.MaiziMybatis.mapper.StudentMapper.selectStudent",1);

来访问这个mapper中的方法,效果是一样的

参考:https://www.cnblogs.com/shenzhi/p/6935071.html

mapper类的每个方法的元数据对象创建

 

在org.apache.ibatis.session.Configuration类中,存储着所有的mybatis配置信息。其中有一个mappedStatments的Map,存储着所有的 mapper方法 的元数据信息,包括返回结果对象类型,方法的id,sql,参数类型,等。

Mapper.xml与Mapper建立联系主要的入口有三:
1)MapperScannerConfigurer扫描Bean流程中,在调用MapperReigistry#addMapper时如果Mapper对应的映射文件(Mapper.xml)未加载到内存,会触发加载。
2)实例化SqlSessionFactory时,如果配置了mapperLocations。
3)示例化SqlSessionFactory时,如果配置了configLocation。

 

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第6张图片

 

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第7张图片


参考:https://blog.csdn.net/prestigeding/article/details/90488395

 

mapper 类动态代理对象生成

(调用getMapper时创建代理对象,并返回,下次调用还会再次创建返回,在spring中以bean单例形式存在,不会重复创建)

(1)使用getMappr方法,传出mapper接口类。

(2)返回mapper接口类被MapperProxy代理后的对象。

主要解析元方法的sql(根据sql所属的方法名(Id)获得MappedStatement对象,此对象中包含一个xml增删改操作(方法)的元数据信息),然后使用SqlSession 进行执行sql,获得返回数据

 

DefaultSqlSession.java
public  T getMapper(Class type) {
  return configuration.getMapper(type, this);
}

Configuration.java
public  T getMapper(Class type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

MapperRegistry.java
public  T getMapper(Class type, SqlSession sqlSession) {
  final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}


MapperProxyFactory.java
public T newInstance(SqlSession sqlSession) {
  final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

 new MapperProxy(sqlSession, mapperInterface, methodCache)的过程如下

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第8张图片

至此,mapper 接口是如何被 mybatis 代理的一目了然。

同时可以看出获取到的代理对象就是 MapperProxy 类,该类实现 InvocationHandler 接口和 invoke 方法。获取到了 MapperProxy 对象之后,在调用该代理对象的接口方法时,只要在这个对象的 invoke 方法里执行相应的 SQL 语句并将结果集返回不就达到我们的目的了吗,因此也就不需要接口实现类了。

参考:https://www.jianshu.com/p/93e18dcc7c10

查询流程

总结下来Spring 中使用mapper接口类查询的步骤如下:

(1)使用mapper接口对象在spring中的Bean是mapper类的代理类 MapperProxy实例,调用对象查询方法selectABC。

(2)实际上会调用MapperProxy的invoke反射的方法。

(3)在invoke方法中会最终调用DefaultSqlSession类中的select方法,此方法中configuration对象根据selectABC方法的唯一id,获得MappedStatement元数据信息。并用BaseExecutor(SqlSession中已经注入executor)执行器执执行query并传入 MappedStatement。

(4)接着通过configuration.newStatementHandler会生成一个StatementHandler对象 ,并在构造器中调用生成parameterHandler和resultSetHandler,同时存储在StatementHandler对象中。

(5)然后用handler生成一个java.sql.statement

(6)最后试用statemenet执行sql。

 

(1)使用mapper类的动态代理实例调用查询方法。

在调用studentMapper.selectWithCondition(studentSelect)的时候,studentMapper只是个接口,我们并没有具体去实现这个接口,这个mybatis使用了动态代理,真正执行的是MapperProxy的invoke方法,如下。

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第9张图片

 (2)继续跟踪mapperMethod.execute方法

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第10张图片

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第11张图片

(3) 然后进入到了DefaultSqlSession类中,为SqlSession的实现类

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第12张图片

继续追踪到了BaseExecutor类,Executor接口的实现类

(4)最终找到了这样一个方法,创建StatementHandler
mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第13张图片

(5)继续

 最终会把所有StatementHandler 类型的拦截都加进去mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第14张图片

继续追踪(5)中new RoutingStatementHandler(),最终看到如下:

在StatementHadler的构造器中调用生成parameterHandler和resultSetHandler,同时存储在StatementHandler对象中。

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第15张图片

 

继续回到步骤(4)看一下java.sql.Statement的生成

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第16张图片

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第17张图片

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第18张图片

继续回到步骤(4) ,看一下query最终的执行,

StatementHandler接口的实现类 org.apache.ibatis.executor.statement.SimpleStatementHandler类最终调用java.sql。Statement的实现类执行sql。

 

 

 

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第19张图片

拦截器原理

 

 MyBatis 通过提供插件机制,让我们可以根据自己的需要去增强 MyBatis 的功能。需要注意的是,如果没有完全理解 MyBatis 的运行原理和插件的工作方式,最好不要使用插件,因为它会改变系底层的工作逻辑,给系统带来很大的影响。

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)


通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }
 
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}

 



  
    
  


上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行底层映射语句的内部对象。

四大对象什么时候被代理,也就是:代理对象是什么时候创建的?

Executor 是 openSession() 的 时 候 创 建 的 ,创建之后即调用 InterceptorChain.pluginAll(),返回层层代理后的对象。

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);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }


StatementHandler 是SimpleExecutor.doQuery()创建的,里面包含了处理参数的 ParameterHandler 和处理结果集的 ResultSetHandler 的创建,创建之后即调用 InterceptorChain.pluginAll(),返回层层代理后的对象。

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;
 
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();
 
    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }
 
    this.boundSql = boundSql;
 
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }
 
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;
  }


具体看看interceptorChain.plginAll(Object target)具体做了什么事情:

public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }


就是调用了自定义插件列表中每个插件的plugin(Object target)方法:

default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }


Plugin.wrap(Object target, Interceptor interceptor)方法调用了JDK的动态代理创建了一个目标对象的代理(Plugin类肯定实现了InvocationHandler方法):

public static Object wrap(Object target, Interceptor interceptor) {
    Map, Set> signatureMap = getSignatureMap(interceptor);
    Class type = target.getClass();
    Class[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }


当被拦截的四大对象被执行前,会先调用Plugin的invoke方法,我们看下源码:

public class Plugin implements InvocationHandler {
 
  private final Object target;
  private final Interceptor interceptor;
  private final Map, Set> signatureMap;
 
  private Plugin(Object target, Interceptor interceptor, Map, Set> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }
 
  public static Object wrap(Object target, Interceptor interceptor) {
    Map, Set> signatureMap = getSignatureMap(interceptor);
    Class type = target.getClass();
    Class[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }
 
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
 
  private static Map, Set> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    if (interceptsAnnotation == null) {
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
    }
    Signature[] sigs = interceptsAnnotation.value();
    Map, Set> signatureMap = new HashMap<>();
    for (Signature sig : sigs) {
      Set methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
      try {
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
  }
 
  private static Class[] getAllInterfaces(Class type, Map, Set> signatureMap) {
    Set> interfaces = new HashSet<>();
    while (type != null) {
      for (Class c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class[interfaces.size()]);
  }
 
}


invoke方法首先会获取插件类上的注解上拦截的方法,若是被拦截的方法,则执行插件的intercept(Invocation invocation)方法,

若不是则执行被代理对象的方法。

Invocation:

public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }


Invocation对被代理对象进行封装,将被代理对象、方法和参数列表传入Invocation,若插件内容执行完成之后,可以调用Invocation.proceed()方法进行被代理对象方法执行:

public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }


总结流程:

 mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第20张图片

参考:https://blog.csdn.net/lihaiyang_sz/article/details/106729017

 

拦截器其他

1. 在mybatis中可被拦截的类型有四种(按照拦截顺序):

  1. Executor:拦截执行器的方法。
  2. ParameterHandler:拦截参数的处理。
  3. ResultHandler:拦截结果集的处理。
  4. StatementHandler:拦截Sql语法构建的处理。

 

多个插件拦截的顺序的问题

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第21张图片

 

需要注意的是,因为拦截器Aa和拦截器Bb均是拦截的StatementHandler对象,所以拦截器B在此获取StatementHandler的时候,获取的是代理对象。

2. 拦截器可拦截的方法:

拦截的类 拦截的方法
Executor update, query, flushStatements, commit, rollback,getTransaction, close, isClosed
ParameterHandler getParameterObject, setParameters
StatementHandler prepare, parameterize, batch, update, query
ResultSetHandler handleResultSets, handleOutputParameters

Executor 中会生成 StatementHandler 并调用,在new StatementHandler会生成ParameterHandler和ResultSetHandler

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第22张图片

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第23张图片

 

mybatis 初始化,与生命周期,mapper映射创建,代理创建,curd执行过程,拦截器原理_第24张图片

你可能感兴趣的:(mybatis,mybatis)