一:引言
1:本文目的
熟悉花哥的都知道,花哥是该粗的地方粗该细的地方细,写文章的话咱追求的还是细致和全面,所以本文意在将Mybatis从SqlSessionFactory被创建出来到Dao被执行成功,我们会将绝大部分的细节写的到位,这是我们本专栏也是本篇文章的目的。
2:首要知识回顾
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();
3:知识补充
1):Mybatis如何处理XML文件
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当中的最核心的两个存储类对象。
2):测试使用Mybatis中API解析XML
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 对应 <user> // /configuration/*,要读取这个根标签下的所有的内容 List<XNode> xNodes = xPathParser.evalNodes("/users/*"); //要读取这个/users标签中下的所有的标签。 List<com.baizhiedu.xml.User> users = new ArrayList<>(); //这里面每一个xNode对应就是一个<user> 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>sunshuai</name> <password>2222</password> </user> <user> <name>xiaohei</name> <password>2222</password> </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内存中,封装到对应的对象当中。
//----------------------------删除------------------------------------//
三:Mybatis核心运行过程
1:new SqlSessionFactoryBuilder().build(inputStram)详解
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对象创建出来了。
1):new XMLConfigBuilder()
对于处理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)); }
2):XMLConfigBuilder.parse()
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的说明不够详细,我们后续会将详解版专门写一篇文章,链接至于此处。
2):this.build(parser.parse());
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
在这里SqlSessionFactory也基于重载build(Configuration config)方法被创建出来。
2:sqlSessionFactory.openSession详解
待补充!
3:sqlSession.getMapper(UserDao.class)详解
我们基于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); }
这里值得注意的是
4:userDao.query()详解
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
备注:此文章未完待续…