本文共 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解析输入流解析到Configurationpublic 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) 截取部分构建过程
加载配置键值对到configurationprivate 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); } } }
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查找。
publicT 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 MapperProxymapperProxy = 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); privateObject 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进行操作,其中结合了一些缓存,参数处理 等等其他操作。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。
publicList 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
privateList 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,并且最后的返回值处理
publicList 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/