Mybatis工作原理流程
1、全局配置文件解析
首先会解析全局配置文件mybatis-config.xml
,下面我们来看看源码。现有下面的调用方式进行查询:
public void queryBlog() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
BlogExample example = new BlogExample();
List<Blog> list = mapper.selectByExample(example);
System.out.println(list);
} finally {
session.close();
}
}
我们跟进SqlSessionFactoryBuilder的build()方法:
public SqlSessionFactory build(InputStream inputStream) {
//调用类中重载的build方面
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//XMLConfigBuilder它时负责解析全局配置文件的类,跟进XMLConfigBuilder类中
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 XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
//发现会new Configuration()对象
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
this.localReflectorFactory = new DefaultReflectorFactory();
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
看到new Configuration()对象时,可以猜到这个就是全局配置文件映射类。
我们回到SqlSessionFactoryBuilder的build()方法。
var5 = this.build(parser.parse());
可以看到parse()就是具体的解析配置文件的方法,继续跟进进入XMLConfigBuilder
解析这部分我们就不细看了,就以解析mappers
为例:可以看到会将mapper对象放入Configuration的mapperRegistry
属性中。
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
//再跟进看看addMapper()方法。
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//knownMappers为HashMap,key为mapper接口类型,value为接口的工厂类。
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
通过对节点的解析,然后将属性赋值给Configuration对象中的属性。解析完成后,调用build()方法。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
DefaultSqlSessionFactory是SqlSessionFactory接口的默认实现。下面通过时序图更直观的来看看:
2、创建会话
SqlSession session = sqlSessionFactory.openSession();
点进跟进查看openSession()的实现,可以在DefaultSqlSessionFactory类中看到如下方法:
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);
//执行器,Exector是直接操作数据库的接口类,
Executor executor = this.configuration.newExecutor(tx, execType);
//返回会话DefaultSqlSession
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;
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (this.cacheEnabled) {//是否启用二级缓存
executor = new CachingExecutor((Executor)executor);
}
//this.interceptorChain是插件拦截器链
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
Executor类关系图如下:BaseExecutor为抽象类
创建会话时序图如下:
3、获取代理对象
会话创建完成后,接下来我们继续看看如何通过getMapper()获得mapper接口的。
BlogMapper mapper = session.getMapper(BlogMapper.class);
点进跟进,在DefaultSqlSession类中。
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
然后从Configuration配置对象的mapperRegistry
属性中取出,在前面第一步解析配置文件是,将mapper接口放入了HashMap中。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
继续跟进源码,在MapperProxyFactory类中发现了,看到如下代码,就豁然开朗了,这是JDK动态代理的实现啊,也就是最后返回了代理对象。
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
接着我们看看MapperProxy类,最后会带哦用invoke()方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
看看这部分的时序图: