mybatis 介绍以及框架源码的学习指标
MyBatis 是以后最风行的 java 长久层框架之一,其通过 XML 配置的形式打消了绝大部分 JDBC 反复代码以及参数的设置,后果集的映射。尽管 mybatis 是最为风行的长久层框架之一,然而相比其余开源框架比方 spring/netty 的源码来说,其正文相对而言显得比拟少。为了更好地学习和了解 mybatis 背地的设计思路,作为高级开发人员,有必要深入研究理解优良框架的源码,以便更好的借鉴其思维。同时,框架作为设计模式的次要利用场景,通过钻研优良框架的源码,能够更好的体会设计模式的精华。学习框架源码和学习框架自身不同,咱们不仅要首先比拟粗疏残缺的相熟框架提供的每个个性,还要了解框架自身的初始化过程等,除此之外,更重要的是,咱们不能泛泛的疾速浏览哪个性能是通过哪个类或者接口实现和封装的,对于外围个性和初始化过程的实现,咱们应该达到下列指标:
- 对于外围个性和外部性能,具体是如何实现的,采纳什么数据结构,边钻研边思考这么做是否正当,或者不合理,尤其是很多的个性和性能的调用频率是很低的,或者很多汇合中蕴含的元素数量在 99% 以上的状况下都是 1,这种状况下很多设计决定并不需要特地去抉择或者适宜于大数据量或者高并发的简单实现。对于很多外部的数据结构和辅助办法,不仅须要晓得其性能自身,还须要晓得他们在上下文中施展的作用。
- 对于外围个性和外部性能,具体实现采纳了哪些设计模式,应用这个设计模式的合理性;
- 绝大部分框架都被设计为可扩大的,mybatis 也不例外,它提供了很多的扩大点,比方最罕用的插件,语言驱动器,执行器,对象工厂,对象包装器工厂等等都能够扩大,所以,咱们应该晓得如有必要的话,如何依照要求进行扩大以满足本人的需要。
2. 从一个实例开始
@Slf4j
public class HelloWorld {public static void main(String[] args) {
String resource = "config.xml”;// 配置文件
Reader reader;
try {
//2. 读取配置文件
reader = Resources.getResourceAsReader(resource);//2. 读取配置文件
//3. 构建 SqlSessionFactory
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
//4. 创立一个 SqlSession
SqlSession sqlSession = sqlMapper.openSession();
try {
//5. 执行一个 Query
User user = sqlSession.selectOne("com.blueheart.mybatis.mapper.UserMapper.getUser", 1);
log.error("user id={},name={}",user.getId(),user.getName());
}finally {
//6. 完结敞开
sqlSession.close();}
} catch (IOException e) {e.printStackTrace();
}
}
}
从上述代码过程中,咱们看看这个过程波及几个对象及相干的办法
0.config.xml
1.Resources.getResourceAsReader(resource);
2.SqlSessionFactory
3.SqlSessionFactoryBuilder
4.SqlSession
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
剖析代码的这一行,从一个资源返回一个 Reader(字符流)
//2. 读取配置文件
reader = Resources.getResourceAsReader(resource);//2. 读取配置文件
Resources.class
这个类是读取资源类,具体实现将 xml 文件加载生成一个 Reader,这里我就有一个疑难,为什么大部分的框架都要本人弄一个 Resource 类呢?加载一个文件或 URL 资源都是比较简单的?真是这样子吗?咱们来看看 Resource.class 的源代码:
/**
* A class to simplify access to resources through the classloader.
* 一个简略的通过类加载器拜访资源的类
*
* @author Clinton Begin
*/
public class Resources {
// 类加载器包装类?为什么,咱们接前面再看
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
/**
* Charset to use when calling getResourceAsReader.
* null means use the system default.
* 字符集,默认应用零碎的,思考得比拟周全,个别咱们都没有思考或间接写死
*/
private static Charset charset;
public static URL getResourceURL(ClassLoader loader, String resource) throws IOException {
// 把返回的过程委托给了 classLoaderWrapper
URL url = classLoaderWrapper.getResourceAsURL(resource, loader);
if (url == null) {throw new IOException("Could not find resource" + resource);
}
return url;
}
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
// 把返回的过程委托给了 classLoaderWrapper
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {throw new IOException("Could not find resource" + resource);
}
return in;
}
/**
* Returns a resource on the classpath as a Properties object
* 失去一个资源的 Properties 对象,资源是.properties 文件
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static Properties getResourceAsProperties(String resource) throws IOException {Properties props = new Properties();
try (InputStream in = getResourceAsStream(resource)) {props.load(in);
}
return props;
}
public static Properties getResourceAsProperties(ClassLoader loader, String resource) throws IOException {Properties props = new Properties();
try (InputStream in = getResourceAsStream(loader, resource)) {props.load(in);
}
return props;
}
public static Reader getResourceAsReader(String resource) throws IOException {
Reader reader;
if (charset == null) {reader = new InputStreamReader(getResourceAsStream(resource));
} else {reader = new InputStreamReader(getResourceAsStream(resource), charset);
}
return reader;
}
public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException {
Reader reader;
if (charset == null) {reader = new InputStreamReader(getResourceAsStream(loader, resource));
} else {reader = new InputStreamReader(getResourceAsStream(loader, resource), charset);
}
return reader;
}
public static File getResourceAsFile(String resource) throws IOException {return new File(getResourceURL(resource).getFile());
}
public static File getResourceAsFile(ClassLoader loader, String resource) throws IOException {return new File(getResourceURL(loader, resource).getFile());
}
这个资源类里最重要的只有几个办法,就是理论执行的办法,这外面有波及另外一个类 ClassLoaderWrapper , 看上去所有这些理论加载都是
咱们再来看看 ClassLoaderWrapper 是如何去理论加载资源的
/**
* A class to wrap access to multiple class loaders making them work as one
* 一个包装拜访多个类加载器使它看起来像在拜访一个一样加载资源
*
* @author Clinton Begin
*/
public class ClassLoaderWrapper {
ClassLoader defaultClassLoader;
ClassLoader systemClassLoader;
ClassLoaderWrapper() {
try {systemClassLoader = ClassLoader.getSystemClassLoader();
} catch (SecurityException ignored) {// AccessControlException on Google App Engine}
}
…
// 外围办法之一,其它一样
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {for (ClassLoader cl : classLoader) {if (null != cl) {
// try to find the resource as passed
InputStream returnValue = cl.getResourceAsStream(resource);
// now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
if (null == returnValue) {returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) {return returnValue;}
}
}
return null;
}
// 外围办法
ClassLoader[] getClassLoaders(ClassLoader classLoader) {return new ClassLoader[]{
classLoader,
defaultClassLoader,
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),
systemClassLoader};
}
}
这个类做了一件什么事件呢?就是按程序找到一个适合的类加载器,去加载相应的资源。最外围的办法就是最初一个 ClassLoader[] getClassLoaders(ClassLoader classLoader), 它返回本人程序的类加载器列表,个别状况下 classLoader 和 defaultClassLoader 都为 null,所以应用最多的就是 Thread.currentThread().getContextClassLoader() 这个类加载器,这里波及一个根底知识点 JVM 类加载过程模模型。
总结
- 框架的资源加载要比个别的应用简单,所以须要有一种机制能正确的加载,首先要解决的就是从不同的 classLoader 加载的问题,以保障能下确找到资源
- 能够返回不同的流,如字节流、字符流、Properties 或 URL
- 转换的过程中要尽能够能思考全面,文件就有编码,所以能够设定指定的编码格局
附加常识
- Java 默认是有三个 ClassLoader,按档次关系从上到下顺次是:
* - Bootstarp ClassLoader
- Ext ClassLoader
- System ClassLoader
* - “双亲委托”
- 所谓“双亲委托”就是当加载一个类的时候会先委托给父类加载器去加载,当父类加载器无奈加载的时候再尝试本人去加载,所以整个类的加载是“自上而下”的,如果都没有加载到则抛出 ClassNotFoundException 异样。
* - 下面提到 Bootstarp ClassLoader 是最顶层的类加载器,实际上 Ext ClassLoader 和 System ClassLoader 就是一开始被它加载的。
* - ClassLoader 称为扩大类加载器,负责加载 Java 的扩大类库,默认加载 JAVA_HOME/jre/lib/ext/ 目下的所有 jar(包含本人手动放进去的 jar 包)。
* - System ClassLoader 叫做零碎类加载器,负责加载应用程序 classpath 目录下的所有 jar 和 class 文件,包含咱们平时运行 jar 包指定 cp 参数下的 jar 包。
类加载器根底