本文主要介绍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> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 上面那个方法只是找到已经创建了Mapper接口并缓存到MapperRegistry,没找到报错
return mapperProxyFactory.newInstance(sqlSession); // 此方法才是Mapper动态代理的核心
}
2) newInstance方法是创建了一个mapperProxy,这个mapperProxy实现了InvocationHandler
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> 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 <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 转换参数
Object param = method.convertArgsToSqlCommandParam(args);
// 执行查询
result = sqlSession.<E>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 <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
// 找mapperStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 调用executor执行查询方法query。
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
}
2) 缓存层的几个调用忽略,实际上调用的是queryFromDatabase
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
return list;
}
3) doQuery 方法是创建StatementHandler,然后执行参数预解析,执行sql,并且最后的返回值处理
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());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
4) 参数解析和返回值处理实际上是StatementHandler的 ParameterHandler和ResultSetHandler的作用,其中涉及到参数处理,用到了TypeHandler。过程省略
网上盗图一张,这张图对执行流程展示是非常直观的。
end!