自此开启MyBatis源码分析,理解大神的架构,写在开头,以此纪念,2020年9月12日08:53:34
一、MyBatis的简单使用
1. 配置文件
主配置文件mybatis_config.xml
以及从配置文件db.properties
,其中db.properties
为数据连接的一些信息。
mybatis_config.xml:
<?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>
<!-- 加载类路径下的属性文件 -->
<properties resource="db.properties"/>
<settings>
<!--关闭一级缓存 -->
<setting name="cacheEnabled" value="false"/>
</settings>
<!-- 设置一个默认的连接环境信息 -->
<environments default="mysql_developer">
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="mysql_developer">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性 -->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="mysql_developer">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性,对应db.properties中的数据库连接信息 -->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- *mapper.xml文件对应的信息 -->
<mapper resource="IPersonMapper.xml" />
</mappers>
</configuration>
db.properties:
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mybatis_analyze
mysql.username=root
mysql.password=123456
2.创建核心启动信息
在创建好数据表和实体类之后,紧接着创建*mapper.xml
文件,此文件对应mapper
接口的查询配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace属性是名称空间,必须唯一,此处为Mapper文件全限定类名 -->
<mapper namespace="com.lightingsui.mapper.IPersonMapper">
<!-- resultMap标签:映射实体与表
type属性:表示实体全路径名
id属性:为实体与表的映射取一个任意的唯一的名字
-->
<resultMap type="com.lightingsui.pojo.Person" id="studentMap">
<!-- id标签:映射主键属性
result标签:映射非主键属性
property属性:实体的属性名
column属性:表的字段名
-->
<id property="id" column="id"/>
<result property="pName" column="p_name"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</resultMap>
<select id="selectAll" resultMap="studentMap">
select * from person
</select>
</mapper>
3.启动类
// 此处填写的名称应该为配置文件的名称
Reader reader = Resources.getResourceAsReader("mybatis_config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<Person> persons = sqlSession.selectList("com.lightingsui.mapper.IPersonMapper.selectAll");
二、配置文件加载源码分析
1.概述
Reader reader = Resources.getResourceAsReader("mybatis_config.xml");
通过加载配置文件,返回了字符流Reader
,透过Resources
源码,发现Resources
可以返回多种格式的IO
- URL**(getResourceURL)**
- InputStream (getResourceAsStream)
- Properties (getResourceAsProperties)
- Reader (getResourceAsReader)
- File (getResourceAsFile)
现在Resources
类的大概作用应该有所了解了,通过输入配置文件名称,从而将配置文件加载为Java
中的IO
,而加载之后的流供给下一步进行解析
2. Resources解析
在Resources中,共有两个属性,其中一个特别重要的属性就是classLoaderWrapper。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Y6N3RKK-1599884242202)(https://raw.githubusercontent.com/lightingsui/Pic/master/img/20200912091806.png)]
关于charset
,它的作用就是在将配置文件转换为Reader(getResourceAsReader)
时生效的,将配置文件转化为指定字符编码的字符,如果不进行设定,那么将使用系统默认的。
charset只在转化为Reader时生效,其它无效
Resources
中的所有方法,内部都是调用了ClassLoaderWrapper
实现的。
3.ClassLoaderWrapper解析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lJ02xCH5-1599884242204)(https://raw.githubusercontent.com/lightingsui/Pic/master/img/20200912093419.png)]
作用
ClassLoaderWrapper
的作用就是选择合适的类加载器,将配置文件加载为二进制流,返回给Resources
,这就是ClassLoaderWrapper
的使命。
字段
defaultClassLoader
将为自定义的默认类加载器,如果不进行设定,那么为null
。
systemClassloader
为系统加载器,在初始化的过程中通过
systemClassLoader = ClassLoader.getSystemClassLoader();
赋值为ApplicationClassLoader
,即系统加载器,主要就是加载classpath
下的类
方法
其中一个最重要的方法就是getClassLoaders
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader, // 参数传递的类加载器
defaultClassLoader, // 默认类加载器
Thread.currentThread().getContextClassLoader(), // 当前线程的类加载器
getClass().getClassLoader(), // 当前类的类加载器
systemClassLoader}; // 系统类加载器
}
这个方法返回特定顺序的类加载器列表,加载配置文件就是从中顺序的选择能够进行加载的类加载器
另一个方法就是加载类 classForName
/**
* 进行类的加载
*
* @param name - 待加载类的名称
* @param classLoader - 类加载器列表
* @return the class
* @throws ClassNotFoundException - 类没有找到,则抛出 {@link ClassNotFoundException}
*/
Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
// 遍历类加载器的列表
for (ClassLoader cl : classLoader) {
if (null != cl) {
try {
// 尝试进行类的加载
return Class.forName(name, true, cl);
} catch (ClassNotFoundException e) {
// we'll ignore this until all classloaders fail to locate the class
}
}
}
throw new ClassNotFoundException("Cannot find class: " + name);
}
另外的一些方法就是将配置文件转化为二进制流,拿一个进行举例,其它同理。
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
return getResourceAsStream(resource, getClassLoaders(classLoader));
}
// 重载得到下面的方法
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
// 拿到类加载器列表后进行遍历
for (ClassLoader cl : classLoader) {
if (null != cl) {
// 尝试加载配置文件
InputStream returnValue = cl.getResourceAsStream(resource);
// 如果没有加载到,则前面添加"/"尝试一次
if (null == returnValue) {
returnValue = cl.getResourceAsStream("/" + resource);
}
// 不为null即加载成功,返回加载后的结果
if (null != returnValue) {
return returnValue;
}
}
}
return null;
}