博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybaits源码分析(一) 核心执行流程
阅读量:4074 次
发布时间:2019-05-25

本文共 9821 字,大约阅读时间需要 32 分钟。

本文主要介绍mybaits的核心执行过程的源码分析,我们按照xml配置的方式,建立一个查询的demo,测试代码如下

@Test	public void test2() throws Exception {		InputStream in = Resources.getResourceAsStream("custom/sqlMapConfig2.xml");		SqlSessionFactory factory2 =  new SqlSessionFactoryBuilder().build(in);		SqlSession openSession = factory2.openSession();		UserMapper mapper = openSession.getMapper(UserMapper.class);		User user = mapper.findUserById(1);		// User user = openSession.selectOne("com.wj.source_two.demo.mapper.UserMapper.findUserById", 1);		System.out.println(user);		openSession.close();	}

    按照上面的代码执行过程,我们把执行过程分为下面几个部分分析: 配置加载,创建mapper代理,SqlSession执行

一、配置加载

1、相关的类

            SqlSessionFactoryBuilder : 负责创建SqlSessionFactory对象,并且传入了从配置文件解析的Configuration。
            XMLConfigBuilder: 核心解析类及其父类BaseBuilder的子类,负责解析xml各种配置到Configuration对象。
            XPathParser:xpath解析
            XNode:node的包装

2、流程分析

  1) SqlSessionFactoryBuilder调用XMLConfigBuilder解析输入流解析到Configuration 

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {                try {                  XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);                  return build(parser.parse());

 2) parse方法是对sqlMapConfig.xml的解析,XPathParser和XNode用法后期补充。

public Configuration parse() {                    parseConfiguration(parser.evalNode("/configuration"));                    return configuration;              }              private void parseConfiguration(XNode root) {                try {                  propertiesElement(root.evalNode("properties")); //issue #117 read properties first                  typeAliasesElement(root.evalNode("typeAliases"));                  pluginElement(root.evalNode("plugins"));                  objectFactoryElement(root.evalNode("objectFactory"));                  objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));                  settingsElement(root.evalNode("settings"));                  environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631                  databaseIdProviderElement(root.evalNode("databaseIdProvider"));                  typeHandlerElement(root.evalNode("typeHandlers"));                  mapperElement(root.evalNode("mappers"));                } catch (Exception e) {                  throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);                }              }

 3) 截取部分构建过程

      加载配置键值对到configuration

  private void propertiesElement(XNode context) throws Exception {                if (context != null) {                  Properties defaults = context.getChildrenAsProperties(); // 得到子节点加载Properties                  String resource = context.getStringAttribute("resource"); // 得到resource属性                  String url = context.getStringAttribute("url"); // 得到url属性                  // ....                  if (resource != null) { // 从资源文件加载Properties                    defaults.putAll(Resources.getResourceAsProperties(resource));                  } else if (url != null) { // 从url属性加载Properties                    defaults.putAll(Resources.getUrlAsProperties(url));                  }                  Properties vars = configuration.getVariables();                  if (vars != null) {                    defaults.putAll(vars);                  }                  parser.setVariables(defaults);                  configuration.setVariables(defaults); // 设置到configuration                }              }

       加载拦截器,解析plugin,并且根据interceptor的类名,进行实例化,而且加载了子元素的配置

     private void pluginElement(XNode parent) throws Exception {         if (parent != null) {                  for (XNode child : parent.getChildren()) {                    String interceptor = child.getStringAttribute("interceptor");                    Properties properties = child.getChildrenAsProperties();                    Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();                    interceptorInstance.setProperties(properties);                    configuration.addInterceptor(interceptorInstance);                  }               }          }

二、创建mapper代理

    1、主要工作类:

             MapperRegistry:configuration的属性(1对1),用于管理已经创建的MapperProxyFactory
             MapperProxyFactory:代理创建的工厂类,代理实现类是MapperProxy、代理方法实际调用类是MapperMethod。
             MapperProxy:实现InvocationHandler的增强,并且缓存了MapperMethod
             MapperMethod:包装原接口方法,配置,session等参数,实现通用excute方法执行,给MapperProxy使用。

   2、执行流程分析

 1) 找Mapper,UserMapper mapper = openSession.getMapper(UserMapper.class), 实际上是MapperRegistry中Map<Class<?>, MapperProxyFactory<?>> knownMappers查找。

      public 
T getMapper(Class
type, SqlSession sqlSession) {                 final MapperProxyFactory
mapperProxyFactory = (MapperProxyFactory
) knownMappers.get(type);                 // 上面那个方法只是找到已经创建了Mapper接口并缓存到MapperRegistry,没找到报错                 return mapperProxyFactory.newInstance(sqlSession); // 此方法才是Mapper动态代理的核心             }

2) newInstance方法是创建了一个mapperProxy,这个mapperProxy实现了InvocationHandler

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

3) 上面就是创建代理类过程,我们重点看看MapperProxy的invoke实现

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {			    if (Object.class.equals(method.getDeclaringClass())) { 			      try { // Object方法直接放行。			        return method.invoke(this, args);			      } catch (Throwable t) {			      }			    } // 创建MapperMethod并且缓存(缓存到MapperProxy对象中)			    final MapperMethod mapperMethod = cachedMapperMethod(method);			    return mapperMethod.execute(sqlSession, args);			  }

4)     MapperMethod的分析

4.1) execute方法一览			  public Object execute(SqlSession sqlSession, Object[] args) {			    Object result;			    if (SqlCommandType.INSERT == command.getType()) {			      Object param = method.convertArgsToSqlCommandParam(args);			      result = rowCountResult(sqlSession.insert(command.getName(), param));			    } else if (SqlCommandType.UPDATE == command.getType()) {			      Object param = method.convertArgsToSqlCommandParam(args);			      result = rowCountResult(sqlSession.update(command.getName(), param));			    } else if (SqlCommandType.DELETE == command.getType()) {			      Object param = method.convertArgsToSqlCommandParam(args);			      result = rowCountResult(sqlSession.delete(command.getName(), param));			    } else if (SqlCommandType.SELECT == command.getType()) {			      if (method.returnsVoid() && method.hasResultHandler()) {			      } else if (method.returnsMany()) {			        result = executeForMany(sqlSession, args); // 查询方法			      } else if (method.returnsMap()) {			      } else {			      }			    } else {			      throw new BindingException("Unknown execution method for: " + command.getName());			    }			    return result;			  }					 4.2) 我们从 execute方法看到	insert方法是调用session.insert(command.getName,param),这个name是什么?			  public SqlCommand(Configuration configuration, Class
mapperInterface, Method method) { String statementName = mapperInterface.getName() + "." + method.getName(); //就是接口名+方法名 4.3) 在看看查询的方法 result = executeForMany(sqlSession, args); private
Object executeForMany(SqlSession sqlSession, Object[] args) { List
result; // 转换参数 Object param = method.convertArgsToSqlCommandParam(args); // 执行查询 result = sqlSession.
selectList(command.getName(), param); // ..... return result; }

5、总结:

                mybaits动态代理mapper就是根据自己设定的mapper接口,给每个类型创建一个动态代理类,这个代理类实际上调用的方法是sqlsession调用interface+method的id进行操作,其中结合了一些缓存,参数处理 等等其他操作。

 三、SqlSession执行过程

1、主要工作类

            DefaultSqlSession: 包装Executor

            Executor: 包装Transaction, 核心调用类,增删改查等操作都是由此类调用的。
            MappedStatement :包装sql,sql类型,返回值类型,参数映射 (对应配置文件一个select等标签)。
            BoundSql : 包装sql和参数映射和参数值。(由不同的SqlSource生成同一类型BoundSql)。
            StatementHandler : 参数处理和执行,内含参数处理、sql执行、返回值处理。
                ParameterHandler: 参数处理
                ResultSetHandler: 结果处理
            TypeHandler: 类型转换

2、执行过程

1) selectOne实际上调用selectList,参数id可以从Configuration中找到MappedStatement。

public 
List
selectList(String statement, Object parameter, RowBounds rowBounds) { // 找mapperStatement MappedStatement ms = configuration.getMappedStatement(statement); // 调用executor执行查询方法query。 List
result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; }

2) 缓存层的几个调用忽略,实际上调用的是queryFromDatabase

private 
List
queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); return list; }

3) doQuery 方法是创建StatementHandler,然后执行参数预解析,执行sql,并且最后的返回值处理

public 
List
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()); return handler.
query(stmt, resultHandler); } finally { closeStatement(stmt); } }

4) 参数解析和返回值处理实际上是StatementHandler的    ParameterHandler和ResultSetHandler的作用,其中涉及到参数处理,用到了TypeHandler。过程省略

网上盗图一张,这张图对执行流程展示是非常直观的。

 

end!

 

 

 

 

转载地址:http://bruni.baihongyu.com/

你可能感兴趣的文章
本地推送
查看>>
FMDB的使用
查看>>
UIImage存为本地文件与UIImage转换为NSData
查看>>
[转]打印质数的各种算法
查看>>
[转]javascript with延伸的作用域是只读的吗?
查看>>
php的autoload与global
查看>>
IE不支持option的display:none属性
查看>>
[分享]mysql内置用于字符串型ip地址和整数型ip地址转换函数
查看>>
TableDnd(JQuery表格拖拽控件)应用进阶
查看>>
[转]开源中最好的Web开发的资源
查看>>
Docker上部署SpringBoot项目并推送镜像到Docker Hub上---以MacOS为例
查看>>
bibtex I was expecting a `,‘ or a `}‘ 问题解决
查看>>
sql server中各类范式的理解
查看>>
Python中列表元素删除
查看>>
二分查找与递归式二分查找
查看>>
在Navicat for MySQL中修改表的编码格式
查看>>
补充另一版ArrayList的初始化过程
查看>>
java接口不能实例化原因浅谈
查看>>
Https加密及攻防
查看>>
Java生成随机不重复推广码邀请码
查看>>