Mybatis运行主要有三步:
1)创建SqlSessionFactory工厂对象
一般使用的方法是使用SqlSessionFactoryBuilder生成器的build方法加载配置文件生成:
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis_config.xml"));
实际调用的是build方法的一个三参数重载方法,该方法使用XMLConfigBuilder类对配置文件进行解析,然后创建了一个DefaultSqlSessionFactory对象:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
parse方法实际调用了parseConfiguration方法,对每一种标签分别进行解析,并将解析结果存入父类BaseBuilder的configure对象中:
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.settingsElement(root.evalNode("settings"));
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
public abstract class BaseBuilder {
protected final Configuration configuration;
……
}
parseConfiguration中调用的各方法都差不多,就是对DOM树进行解析
2)创建SqlSession对象
SqlSession对象通过SqlSessionFactory的工厂方法openSession产生:
SqlSession sqlSession=sqlSessionFactory.openSession();
此方法还可以接受一些参数,如:
- boolean autoCommit:是否自动提交事务,默认false
- TransactionIsolationLevel level:事务隔离级别,默认null(为空时会从Connection获取事务隔离级别)
- ExecutorType execType:Executor类型,默认SimpleExecutor
接受以上三个参数的openSession方法会调用openSessionFromDataSource方法进一步处理
还可以接受一个Connection对象,从连接中获取以上属性进行配置,过程差不多,以openSessionFromDataSource为例:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
可以看到方法中创建了一个DefaultSqlSession对象并进行组装
3)创建Mapper对象
使用getMapper方法,定义如下:
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
实际是从configuration中取出Mapper对象:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry类内部使用一个HashMap存储MapperProxyFactory对象(Mapper的类型为key)
MapperProxyFactory是在创建SqlSessionFactory时,在parseConfiguration -> mapperElement方法中逐个添加到MapperRegistry的:
private void mapperElement(XNode parent) throws Exception {
……
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
……
}
addMapper方法只会对接口进行处理。
在getMapper方法中创建Mapper代理对象:
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
这里看起来创建的是MapperProxyFactory的代理,实际上是用了重载函数的障眼法隐藏了实现:
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
MapperProxy才是真正实现了InvocationHandler的动态代理类,其invoke方法有一个分支判断:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
意思是:
如果是创建代理对象(Proxy.newProxyInstance方法最终是向newInstance方法传递了一个Object数组,所以会进入if分支),就按照动态代理流程往下走
如果是方法调用(比如调用Mapper的某个方法),就调用executor方法执行(该方法根据MapperMethod的类型再调用sqlSession封装的CRUD方法),代码如下:
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
if (SqlCommandType.INSERT == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
} else if (SqlCommandType.UPDATE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
} else if (SqlCommandType.DELETE == this.command.getType()) {
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
} else {
if (SqlCommandType.SELECT != this.command.getType()) {
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
代码看着很多,但是都是命令模式的各个分支,做的事情都是组装参数和结果集,无非是根据查询需要调用不同的组装方法而已
举例:
UserMapper::getUser方法,其sql语句为:
select * from User where id=#{id};
是一个select方法,另外由于id为主键,查询结果必然只有一条,所以其command类型为SELECT,method对象(方法特征)的returnsMany、returnsVoid、returnsMap属性可推断均为false,执行的必然是selectOne方法所在分支
在此打断点DEBUG,确实停住了,并且相关属性完全符合推断: