时隔许久,终于又能接着来搞他啦。Mybatis 一起来探究吧。 先笑会再进入主题吧
开始啦
String resource="mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
public SqlSessionFactory build(InputStream inputStream) {
//在这里又接着调用了重载方法build 我们接着往下走啊
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//XMLConfigBuilder是对mybatis的配置文件进行解析的类,会对myabtis解析后的信息存放在Configuration对象中
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 下面这句到重点啦
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
一个一个讲啊,慢慢来。
XMLConfigBuilder : 翻译过来就是XML配置器
XMLConfigBuilder是对mybatis的配置文件进行解析的类,会对myabtis解析后的信息存放在Configuration对象中,Configuration对象
会贯穿整个mybatis的执行流程,为mybatis的执行过程提供各种需要的配置信息。
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
XPathParser 和 XMLMapperEntityResolver 不是主要研究对象,我放在了文章最后,好奇的话,可以先滑到文末去看。
先看parser.parse()方法
它的返回对象是核心配置对象Configuration
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
可以看到 在这里就是将我们配置的好的文件 一步一步解析封装到Configuration对象去。
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
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);
}
}
这一段代码主要作用就是将所有的配置文件加载到给定的对象。
我对于研究这个非常不熟练,结果就是走不出来。
mapperElement(root.evalNode("mappers"))
里面要研究的东西不少,先略过哈。
我们可以知道 parser.parse()
构建出了一个 Configuration
对象,并成为了build()方法的入参。
这个build(parser.parse());
是
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
这个地方就是将之前已经赋值好的Configuration
放在里面
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
地点:长沙
至此 我们获得了 SqlSessionFactory 对象,我们可以通过这获取到mybatis操作数据库的 SqlSession 对象。
SqlSession sqlSession = sessionFactory.openSession();
openSession()
是SqlSessionFactory 接口下的方法
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
我们这里调用的方法是这个哈
@Override
public SqlSession openSession() {
// openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
最后一个参数就是是否提交,如果不为true,我们做了修改or删除,数据库并不会修改or删除。
接着往下看:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
最后返回mybatis操作数据库的 SqlSession 对象。
/**
检索映射器。
参数:
type – 映射器接口类
类型参数:
<T> – 映射器类型
返回:
绑定到此 SqlSession 的映射器
*/
<T> T getMapper(Class<T> type);
接下来开始套娃哈
我们实际调用的是它的实现类里的方法:
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
你看又牵扯到configuration
对象了哈,知道它多重要了吧。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
到了mapperRegistry
这个对象拉。 mapperRegistry的翻译就是映射器注册表 这个地方我目前还没有搞懂它是什么时候注册进去的哈。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 这里是去获得 我们传过来的 MyUserMapper.class的代理类的
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
接下来继续看这个 mapperProxyFactory.newInstance(sqlSession) 方法
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
接着套丫
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
当我们看到这里的时候就知道mybatis是通过反射和代理来进行这些操作的哈。
真实执行对象并不在,而是在 MapperProxy mapperProxy Mapper的代理对象中。
那个具体执行的,等下一篇博客哈。我还有很多没懂的。
public class XPathParser {
private final Document document;
private boolean validation;
private EntityResolver entityResolver;
private Properties variables;
private XPath xpath;
}
对这个我没有深究。doc注释就是这么一句话:MyBatis DTD 的离线实体解析器 说实话DTD都是我第一次见 🤦
当然百度是强大的。
它是EntityResolver
子类,xml 的解析会基于事件触发对应的 Resolver 或 Handler,当解析到 dtd 等外部资源时会
EntityResolver
的resolveEntity
方法。在XMLMapperEntityResolver.resolveEntity
中,当解析到 mybatis-3-config.dtd、
mybatis-3-mapper.dtd 等资源时,会直接从 classpath 下的 org/apache/ibatis/builder/xml/ 路径获取资源,而不需要通过 url 获取。
涉及到的类包括:
MapperBuilderAssistant:Mapper文件解析辅助类,包括了解析各种节点的方法。
BaseBuilder:XMLMapperBuilder与MapperBuilderAssistant的父类,保存了Configuration、TypeAliasRegistry、TypeHandlerRegistry三个属性。
TypeAliasRegistry:别名对应关系,保存了字符串与类的对应信息,例如“String”字符串对应 String.class
TypeHandlerRegistry:注册java.lang与java.sql类型对应关系
Configuration:Mapper文件解析节点完成后,存储解析后的属性信息。
XMLStatementBuilder:解析SQL语句节点类,解析后存储在Configuration下的mappedStatements属性
XMLMapperBuilder用于解析Mapper文件,包括namespace、parameterMap、resultMap以及各种SQL(insert/uppdate/delete/select)
解析过程中主要是parse()方法,
解析Mapper文件后会存储在MapperBuilderAssistant以及它父类BaseBuilder的实例下,主要是Configuration类
我这算是自己的阅读笔记哈,无聊读着看看。
算是我第一次读源码的记录。
大家一起加油哦。