本文承接上一文章的小节继续展开Mybatis(调用简图和框架设计思路)+手写模拟mybatis源码分享_喜欢火影的木易杨的博客-CSDN博客
我们结合前面对Mybatis设计的分层结构猜想,参考mybatis源码进行手写,支持通过sqlSession查询数据库,也支持模拟Spring整合扫描@Mapper的注解通过mapper接口直接查询数据库。本小节代码会进行详细讲解,代码在CSDN链接手写简易mybatis-Java文档类资源-CSDN下载。
首先,整体代码的设计思路紧紧围绕下图展开:
本项目为了方便运行构建,使用SpringBoot构建。整体代码分层如下:
还是从TestMybatis.java展开:
import com.test.mapper.BlogMapper;
import com.test.model.Blog;
import custom.annotation.Mapper;
import custom.annotation.MapperScan;
import custom.io.Resources;
import custom.session.CustomSqlSession;
import custom.session.SqlSessionFactory;
import custom.session.SqlSessionFactoryBuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
@ComponentScan(basePackages={"custom","com.test"})
@MapperScan(value="com.test", annotationClass= Mapper.class)
public class TestMybatis {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext context = SpringApplication.run(TestMybatis.class);
//得到SqlSession
CustomSqlSession sqlSession = context.getBean(CustomSqlSession.class);
//通过namesace直接查询
Blog blog2 = (Blog) sqlSession.selectOne("com.test.model.BlogMapper.selectTestById", 2);
System.out.println(blog2);
//mapper接口获取代理类,通过代理类查询
Blog query = new Blog();
query.setId(1);
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Blog blog3 = blogMapper.selectBlog(query);
System.out.println(blog3);
}
@Bean
public SqlSessionFactory getSqlSessionFactory() throws IOException{
//解析mybatis-config配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
Properties properties = new Properties();
//数据库配置信息
properties.load(Resources.getResourceAsStream("config.properties"));
return new SqlSessionFactoryBuilder().build(is, properties);
}
@Bean("defaultSqlSession")
public CustomSqlSession getCustomSqlSession(SqlSessionFactory sqlSessionFactory) throws IOException {
//得到SqlSession
return sqlSessionFactory.openSession();
}
}
这里就使用了SqlSessionFactoryBuilder、sqlSessionFactory、CustomSqlSession
SqlSessionFactoryBuilder主要接收配置流,在build方法中调用XMLConfigBuilder类进行配置文件解析,同时内部还会调用XMLMapperBuilder类进行mapper文件的解析,解析后的信息放在Configuration类当中。在mybatis源码中使用xpath进行解析,而我们自己手写的则使用dom4j进行节点解析。
接下来是获取sqlSession,通过 sqlSessionFactory.openSession()
创建一个sqlSession实现类,传入需要的configuration配置信息,以及一个JDBC执行器对象。
这样就完成了第一个调用简图的调用过程。其它核心代码可下载完整工程运行查看。
接下来是扫描Mapper注解并生成动态代理类的逻辑。核心思路是定义接口,Mapper注解,SelectOne等注解。在接口上使用@Mapper修饰,在接口方法上使用@SelectOne等注解修饰。就可以直接使用接口.接口方法发起对数据库的请求调用。
定义好之后,在启动类中进行Mapper扫描即可。
使用MapperScannerConfigurer进行扫描,给MapperScannerConfigurer传入必要参数
调用MapperScannerConfigurer,执行postProcessBeanDefinitionRegistry
使用自定义扫描器扫描指定Mapper接口
MapperFactoryBean.java是产生代理的核心类,同时在这个类中还会进行接口类与代理类的绑定并传入map中
这样就完成了mapper接口类与代理ProxyFactory的绑定。那么通过getMapper方法获取是怎么产生代理类的呢?
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
sqlSession类:
configuration类:
MapperRegistry类:动态代理对象就是在这里产生的
MapperProxyFactory类:执行newInstance创建代理对象并返回给getMapper
当getMapper得到的动态代理类执行Blog blog3 = blogMapper.selectBlog(query);就会触发到
InvocationHandlerProxy代理类的invoke方法,因为这个代理类继承了InvocationHandler接口。
在这个InvocationHandlerProxy代理类的invoke方法内,通过传入的sqlSession调用内部的JDBC执行查询并返回结果。
以上就是本手写代码的大致调用过程,本例子有很多细节都没有实现(如入参解析,返回结果解析,update接口功能,动态sql等等),而mybatis使用了Handler等诸多处理类进行优雅处理,mybatis这块的源码也是值得继续深入学习的。
再次附上代码下载地址:手写简易mybatis-Java文档类资源-CSDN下载