熟悉花哥的都知道,花哥是该粗的地方粗该细的地方细,写文章的话咱追求的还是细致和全面,所以本文意在将Mybatis从SqlSessionFactory被创建出来到Dao被执行成功,我们会将绝大部分的细节写的到位,这是我们本专栏也是本篇文章的目的。
1:在我们使用原生Mybatis的时候,我们获取UserDao的实现类对象,还得向下面这么写。基于Spring整合Mybatis之后,我们也就只会拿着userDao.xxx()这样进行操作了。然而,我们要知道下文中几行代码,才能体现Mybatis真正的运行过程。
//通过这一步将Mybatis配置文件读取到我们的JVM中。
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//接下来就该创建SqlSessionFactory和SqlSession对象了。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStram);
SqlSession sql = sqlSessionFactory.openSession);
//获取到具体的UserDao接口的代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
//基于实际的JDBC的
int result = userDao.save();
List<User> users= userDao.query();
1:任何配置读取到JVM当中一定是以对象的形式体现的,封装成一个一个存储类型的对象,存储在JVM的用户内存中。
2:Mybatis的核心配置文件Mybatis-config.xml会被封装成Configuration对象,读取XML相关内容。
3:Mybatis中的Mapper.xml文件则是被打散了,这种标签则是被封装到MappedStatement对象中,剩余的标签则是被封装到
各种类型的对象基于放到Map当中,key是nameSpace.id的形式,并且浙西对象都保存到了Configuration当中,Configuration和MappedStatement是双向引用。
4:Java当中XML解析的方式有三种:DOM SAX XPath,Mybatis在处理XML的过程当中使用的是Xpath
5:XPathParser解析了Xml之后初步解析成为一个XNode对象。
6:Configuration和MappedStatement是Mybatis当中的最核心的两个存储类对象。
XPathParser xpathPaser = new XPathParser(InputStream)
通过标签Node的方式就可以获取到一个一个标签的具体的内容。
@Test
public void testXml() throws IOException {
//InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//Reader reader = Resources.getResourceAsReader("users.xml");
InputStream inputStream = Resources.getResourceAsStream("users.xml");
XPathParser xPathParser = new XPathParser(inputStream);
//xNodes 里面的xNode 对应
// /configuration/*,要读取这个根标签下的所有的内容
List<XNode> xNodes = xPathParser.evalNodes("/users/*");
//要读取这个/users标签中下的所有的标签。
List<com.baizhiedu.xml.User> users = new ArrayList<>();
//这里面每一个xNode对应就是一个
for (XNode xNode : xNodes) {
com.baizhiedu.xml.User user = new com.baizhiedu.xml.User();
List<XNode> children = xNode.getChildren();
user.setName(children.get(0).getStringBody());
user.setPassword(children.get(1).getStringBody());
users.add(user);
}
for (com.baizhiedu.xml.User user : users) {
System.out.println("user = " + user);
}
}
<users>
<user>
<name>sunshuainame>
<password>2222password>
user>
<user>
<name>xiaoheiname>
<password>2222password>
user>
users>
以上这个内容是在对应到Mybatis框架当中是在:build方法当中完成的InputStream – XNode – Configuration的解析。
//----------------------------删除------------------------------------//
整个Mybatis的操作就是两大块,一个是获取Dao之后的操作,另外一个就是获取SqlSession的操作,把这几大块内容搞清楚了,我们大致的东西就搞明白了。
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
这块是最没有歧义的,这块就是通过IO的方式通过获取Mybatis-config.xml和xxxMapper.xml的中的配置内容。
我们上边虽然只读取了Mybatis.xml配置文件当中的内容,但是顺便也会把Mapper.xml当中的脚本,这是基于Mybatis-config.xml当中的Mapper标签,将所有的Mapper.xml找到,读取到JVM内存中,封装到对应的对象当中。
//----------------------------删除------------------------------------//
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStram);
build方法是来自于SqlSessionFactoryBuilder,SqlSessionFactory 也是需要SqlSessionFactoryBuilder来进行创建的。
点击去build方法之后我们可以看到:
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
这个方法调用的是下面这个重载的方法:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//parser.parse()的返回值:Configuration
//var5 是SqlSessionFactory
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;
}
到这里我们可以清楚的看到,parser.parse()方法已经将核心存储类Configuration创建出来了,build(Configuration) 将SqlSessionFactory对象创建出来了。
对于处理XML文件Mybatis当中有两个核心的类:XPathParser和XMLConfigBuilder,经过一系列的读取之后,将Mybatis核心配置文件的内容暂时放置到了XMLConfigBuilder.XPathParser.Document当中:
XMLConfigBuilder:
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
XPathParser:
private final Document document;
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
this.commonConstructor(validation, variables, entityResolver);
this.document = this.createDocument(new InputSource(inputStream));
}
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
//parser.evalNode("/configuration")这个是将Mybatis-config.xml中的configuration标签整个拿了出来。
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
parser.evalNode(“/configuration”)这个是将Mybatis-config.xml中的configuration标签整个拿了出来,返回值是一个XNode对象。parseConfiguration(XNode)的返回值是核心的Configuration对象,并且在此处也将关键的 MappedStatement也创建了出来,并在
Configuration对象当中以:
protected final Map<String, MappedStatement> mappedStatements;
存在,至此Mybatis存存储类对象Configuration+MappedStatement都已经创建完毕!
补充说明:此处对于Configuration+MappedStatement的说明不够详细,我们后续会将详解版专门写一篇文章,链接至于此处。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
在这里SqlSessionFactory也基于重载build(Configuration config)方法被创建出来。
待补充!
我们基于sqlSessionFactory.openSession()创建的SqlSession的类型是:DefaultSqlSession,基于上述代码路径一路走到了MapperProxyFactory的类中,这是Mybatis当中创建UserDao代理对象的核心对象之一,另外一个是MapperProxy,基于二者Mybatis创建出来了UserDao的代理对象,最终代码如下:
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
MapperProxy是InvocationHandler的代理类
public class MapperProxy<T> implements InvocationHandler, Serializable {
...
}
Mybatis再次创建接口的代理对象正是基于JDK提供的Proxy.newInstance()的方式:
Proxy.newProxyInstance(ClassLoader classLoader, Class[] clazzs, InvocationHandler invocationHandler);
我们知道JDK创建代理对象的方式是将额外功能书写到了InvocationHandler.invoke()方法中。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
这里值得注意的是
UserDao userDao = sqlSession.getMapper(UserDao.class);以下部分是我们的关注点,我们通过动态代理构建了Dao的实现类,并创建了他的对象。
Mybatis在这里是通过两种关键类型做的
1:MapperProxyFactory(JDK创建的代理 )
2:MapperProxy代理的就是MapperProxy的代理。
3:SqlCommand
|-- type [insert update select delete]
|-- name namespace.id
4:MethodSingnatrue
|-- 方法的返回值
|-- 方法的参数
| -- sqlSession.update();
|-- Excutor (SimpleExcutor,ReuseExcutor....)
|-- StatementHandler
|-- ParameterHandler ResultSetHandler
TypeHandler
备注:此文章未完待续…