allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理

admin 3周前 (06-21) 科技 13 1

目录
  • Mybatis基本熟悉
    • 动态署理
      • JDK实现
      • CGLIB动态署理
      • 总结
    • 反射
  • Configuration工具作用
  • 映射器结构
  • sqlsession执行流程(源码跟踪)
    • Executor
    • StatementHandler
    • 效果处置器(ResultSetHandler)
    • 总结
  • 主题

mybatis运行分为两部门,第一部门读取设置文件缓存到Configuration工具中。用以建立SqlSessionFactory,第二部门是SqlSession的执行历程。

Mybatis基本熟悉

动态署理

  • 之前我们知道Mapper仅仅是一个接口,而不是一个逻辑实现类。但是在Java中接口是无法执行逻辑的。这里Mybatis就是通过动态署理实现的。关于动态署理我们常用的有Jdk动态署理和cglib动态署理。两种却别这里不做赘述。关于CGLIB署理在框架中使用的比较多。

  • 关于动态署理就是所有的请求有一个入口,由这个入口举行分发。在开发领域的一个用途就是【负载平衡】

  • 关于Mybatis的动态署理是使用了两种的连系。

  • 下面看看JDK和cglib两种实现

JDK实现

  • 首先我们需要提供一个接口 , 这个接口是对我们程序员的一个抽象。 拥有编码和改BUG的手段

public interface Developer {

    /**
     * 编码
     */
    void code();

    /**
     * 解决问题
     */
    void debug();
}

  • 关于这两种手段每个人处置方式差别。这里我们需要一个详细的实例工具

public class JavaDeveloper implements Developer {
    @Override
    public void code() {
        System.out.println("java code");
    }

    @Override
    public void debug() {
        System.out.println("java debug");
    }
}

  • 我们传统的挪用方式是通过java提供的new 机制缔造一个JavaDeveloper工具出来。而通过动态署理是通过java.lang.reflect.Proxy工具建立工具挪用现实方式的。

  • 通过newProxyInstance方式获取接口工具的。而这个方式需要三个参数
    ClassLoader loader : 通过现实接口实例工具获取ClassLoader
    Class<?>[] interfaces : 我们抽象的接口
    InvocationHandler h : 对我们接口工具方式的挪用。在挪用节点我们可以举行我们的营业阻挡


JavaDeveloper jDeveloper = new JavaDeveloper();
Developer developer = (Developer) Proxy.newProxyInstance(jDeveloper.getClass().getClassLoader(), jDeveloper.getClass().getInterfaces(), (proxy, method, params) -> {
    if (method.getName().equals("code")) {
        System.out.println("我是一个特殊的人,code之前先剖析问题");
        return method.invoke(jDeveloper, params);
    }
    if (method.getName().equals("debug")) {
        System.out.println("我没有bug");

    }
    return null;
});
developer.code();
developer.debug();

CGLIB动态署理

  • cglib动态署理优点在于他不需要我们提前准备接口。他署理的现实的工具。这对于我们开发来说就很利便了。

public class HelloService {
    public HelloService() {
        System.out.println("HelloService组织");
    }

    final public String sayHello(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }

    public void sayHello() {
        System.out.println("HelloService:sayHello");
    }
}

  • 下面我们只需要实现cglib提供的MethodInterceptor接口,在初始化设置cglib的时刻加载这个实例化工具就可以了

public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======插入前置通知======");
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("======插入后者通知======");
        return object;
    }
}

  • 下面我们就来初始化设置cglib

public static void main(String[] args) {
    //署理类class文件存入内陆磁盘利便我们反编译查看源代码
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/root/code");
    //通过CGLIB动态署理获取署理工具历程
    Enhancer enhancer = new Enhancer();
    //设置enhancer工具的父类
    enhancer.setSuperclass(HelloService.class);
    // 设置enhancer的回调工具
    enhancer.setCallback(new MyMethodInterceptor());
    //建立署理工具
    HelloService helloService = (HelloService) enhancer.create();
    //通过署理工具挪用目的方式
    helloService.sayHello();
}

  • 仔细看看cglib和spring的aop特别像。针对切点举行切面阻挡控制。

总结

  • 通过对比两种动态署理我们很容易发现,mybatis就是通过JDK署理实现Mapper挪用的。我们Mapper接口实现通过署理到xml中对应的sql执行逻辑

反射

  • 信赖有一定履历的Java工程师都对反射或多或少有一定领会。实在从思想上看不惯哪种语言都是有反射的机制的。
  • 通过反射我们就摆脱了工具的限制我们挪用方式不再需要通过工具挪用了。可以通过Class工具获取方式工具。从而通过invoke方式举行方式的挪用了。

Configuration工具作用

  • Configuration工具存储了所有Mybatis的设置。主要初始化一下参数
    • properties
    • settings
    • typeAliases
    • typeHandler
    • ObjectFactory
    • plugins
    • environment
    • DatabaseIdProvider
    • Mapper映射器

映射器结构

  • BoundSql提供三个主要的属性 parameterMappings 、parameterObject、sql

  • parameterObject参数自己。我们可以通报java基本类型、POJO、Map或者@Param标注的参数。

  • 当我们通报的是java基本类型mybatis会转换成对应的包装工具 int -> Integer

  • 若是我们通报POJO、Map。就是工具自己

  • 我们通报多个参数且没有@Param指定变量名则parameterObject 类似
    {"1":p1,"2":p2,"param1":p1,"param2":p2}

  • 我们通报多个参数且@Param指定变量名 则parameterObject类似
    {"key1":p1,"key2":p2,"param1":p1,"param2":p2}

  • parameterMapping 是纪录属性、名称、表达式、javaType,jdbcType、typeHandler这些信息

  • sql 属性就是我们映射器中的一条sql. 正常我们在常见中对sql举行校验。正常不需要修改sql。

sqlsession执行流程(源码跟踪)

  • 首先我们看看我们平时开发的Mapper接口是若何动态署理的。这就需要提到MapperProxyFactory这个类了。该类中的newInstance方式

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  • 通过上满代码及上述对jdk动态署理的表述。我们可以知道mapperProxy是我们署理的重点。
  • MapperProxy是InvocationHandler的实现类。他重写的invoke方式就是署理工具执行的方式入口。

@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);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}


private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
    & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
    && method.getDeclaringClass().isInterface();
}

  • 通过源码发现。invoke内部首先判断工具是否是类 。 通过打断点发现最终会走到cacheMapperMethod这个方式去建立MapperMethod工具。
  • 继续查看MapperMethod中execute方式我们可以领会到内部实现实在是一个下令行模式开发。通过判断下令从而执行差别的语句。判断到详细执行语句然后将参数通报给sqlsession举行sql挪用并获取效果。到了sqlsession就和正常jdbc开发sql举行关联了。sqlsession中ExecutorStatementHandlerParameterHandlerResulthandler四大天王

Executor

  • 顾名思义他就是一个执行器。将java提供的sql提交到数据库。Mybatis提供了三种执行器。

  • Configuration.classnewExecutor源码

  • 凭据uml我们不难看出mybatis中提供了三类执行器划分SimpleExecutor、ReuseExecutor、BatchExecutor

public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 获得configuration 中的environment
      final Environment environment = configuration.getEnvironment();
      // 获得configuration 中的事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 获取执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      // 返回默认的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();
    }
  }

  • 通过上述源码我们知道在sqlsession获取一个数据库session工具时我们或凭据我们的settings设置加载一个Executor工具。在settings中设置也很简单

<settings>
<!--取值局限 SIMPLE, REUSE, BATCH -->
	<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>

  • 我们也可以通过java代码设置

factory.openSession(ExecutorType.BATCH);

StatementHandler

  • 顾名思义,StatementHandler就是专门处置数据库回话的。这个工具的建立照样在Configuration中治理的。

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;
  }

  • 很明显Mybatis中StatementHandler使用的是RoutingStatementHandler这个class

  • 关于StatementHandler和RoutingStatementHandler之间的关系我们通过源码可以看出这里和Executor一样都是适配器模式。接纳这种模式的利益是利便我们对这些工具举行署理。这里读者可以预测一下是使用了哪种动态署理。给点提醒 这里使用了接口哦

allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理 第1张
allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理 第2张

  • 在查看BaseStatementHandler结构我们会发现和Executor一模一样。同样的Mybatis在组织RoutingStatementHandler的时刻会凭据setting中设置来加载差别的详细子类。这些子类都是继续了BaseStatementHandler.

  • 前一节我们跟踪了Executor。 我们知道Mybatis默认的是SimpleExecutor。 StatementHandler我们跟踪了Mybaits默认的是PrePareStatementHandler。在SimpleExecutor执行查询的源码如下

allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理 第3张
allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理 第4张

  • 我们发现在executor查询钱会先让statementHandler构建一个Statement工具。最终就是StatementHandler中prepare方式。这个方式在抽象类BaseStatmentHandler中已经封装好了。

allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理 第5张

  • 这个方式的逻辑是初始化statement和设置毗邻超时等一些辅助作用
  • 然后就是设置一些参数等设置。最后就走到了执行器executor的doquery

allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理 第6张

  • PrepareStatement在我们jdbc开发时是常见的一个类 。 这个方式执行execute前我们需要设置sql语句,设置参数举行编译。这一系列步骤就是适才我们说的流程也是PrepareStatementHandler.prepareStatement帮我们做的事情。那么剩下的我们也很容易想到就是我们对数据效果的封装。正如代码所示下马就是resultSetHandler帮我们做事情了。

效果处置器(ResultSetHandler)


@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

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

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

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

  • 这个方式我们可以导出来是效果xml中标签设置对效果的一个封装。

总结

  • SqlSession在一个查询开启的时刻会先通过CacheExecutor查询缓存。击穿缓存后会通过BaseExector子类的SimpleExecutor建立StatementHandler。PrepareStatementHandler会基于PrepareStament执行数据库操作。并针对返回效果通过ResultSetHandler返回效果数据

allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理 第7张

主题

allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理 第8张

,

Allbet

www.szqygww.com欢迎进入欧博开户平台(Allbet Gaming),欧博开户平台开放欧博(Allbet)开户、欧博(Allbet)代理开户、欧博(Allbet)电脑客户端、欧博(Allbet)APP下载等业务

皇冠APP声明:该文看法仅代表作者自己,与本平台无关。转载请注明:allbetgmaing客户端下载:居然还有人这样解说mybatis运行原理

网友评论

  • (*)

最新评论

  • 环球UGAPP下载 2020-06-21 00:43:24 回复

    欧博亚洲官方注册欢迎进入欧博亚洲官方注册(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。偶然看到的,一直在看

    1

标签列表

    文章归档

    站点信息

    • 文章总数:530
    • 页面总数:0
    • 分类总数:8
    • 标签总数:893
    • 评论总数:151
    • 浏览总数:9270