借鉴了读写分离框架,大概了解了实现原理,顺手写了这篇博客
- Mybitas的Dao层实际上是一个代理对象,在执行的时候先走MapperProxy.invoke()
主要代码
pom.xml
<dependencies>
<!-- 添加log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<!-- 添加mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.6</version>
</dependency>
<!-- 添加mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.12</version>
</dependency>
</dependencies>
mybitas.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>
<!-- 指定Mybatis使用log4j -->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--
如果上面没有指定数据库配置的properties文件,那么此处可以这样直接配置
-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件,mybatis精髓, 后面才会细讲 -->
<mappers>
<mapper resource="com/dao/userDao-mapping.xml"/>
</mappers>
</configuration>
Main
package com;
import com.dao.UserDao;
import com.entity.User;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.*;
import java.io.IOException;
import java.util.Properties;
/**
* @author :liujipeng
* @date :Created in 2019/12/27 14:58
* @description:${description}
*/
public class Application {
public static void main(String[] args) {
SqlSession sqlSession = getSessionFactory().openSession();
UserDao userMapper = sqlSession.getMapper(UserDao.class);
User user = userMapper.findUserById(1);
}
private static SqlSessionFactory getSessionFactory() {
SqlSessionFactory sessionFactory = null;
String resource = "mybitas.xml";
try {
sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));
} catch (IOException e) {
e.printStackTrace();
}
MyInterceptor myInterceptor = new MyInterceptor();
sessionFactory.getConfiguration().addInterceptor(myInterceptor);
return sessionFactory;
}
//拦截器
@Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,
Object.class})})
static class MyInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
}
userDao-mapping.xml
<?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" >
<mapper namespace="com.dao.UserDao">
<select id="findUserById" resultType="com.entity.User" >
select * from user where id = #{id}
</select>
<update id="updateUser" >
update user set name = "sds" where id = #{id}
</update>
</mapper>
关于Mapper.xml 文件的加载和"select|insert|update|delete"
的解析此次先不进行说明,全部被解析后封存在Configuration.mappedStatements<String, MappedStatement>
key就是namespace+id
形式储存的
Interceptor和Plugin
我的application里面在SqlSessionFactory初始化完成的时候会将自己写的 MyInterceptor添加到 InterceptorChain 里面。
在什么时候进行调用呢
在获得SqlSessionFactory后就要获取SqlSession,此时就会发生第一次拦截
根据openSession()方法进来,我们会看到如下得到一个执行器
在打开获取执行器的方法里面我们可以看到获取了所有的拦截器
这个interceptor.plugin(target)就进入了自己设置的interceptor里面
这个Plugin.wrap
方法继续跟进去
signatureMap
获取的是自己在Interceptor的注解上定义的内容,key 就是type值对象,value存的是Executor.class里面通过nethid和args得到的方法
target是newExecutor里面传进去的,有此处可见是CachingExecutor
对象
因此interfaces
里面会得到一个CachingExecutor
数组对象,如下
回头再看这个三元运算符,则会返回Plugin针对CachingExecutor
的代理对象。
然后的方法就是一路返回,直到返回SqlSession
上面这一步返回的是 MapperProxy
的代理对象,在执行xxxMapper.find()
方法的时候会走如下
然后就会走到上面这个地方,这个 executor
就是上面Plugin产生的代理对象,当执行的时候会先走 Plugin.invoke()
method.getDeclaringClass()
得到的是进入Plugin的这个方法的方法对象,在wrap()的时候已经初始化了signatureMap数据,此次是大于0的,因此就又走了
this.interceptor.intercept(new Invocation(this.target, method, args))
这个就是自定义的intercept方法中
至此,mybitas的Interceptor就结束了,后面的就是mybitas 自己的初始化各种链接查询了。