基于上一篇博客,手写MyBatis1.0末尾提出的几个不足之处与需要新增的地方,这篇博客将完善之前的MyBatis1.0版本,升级为2.0版本~将会新增的功能:
- 加入Plugin插件功能。
- 加入缓存功能。
- 分解Executor指责,分出各个类使其符合单一职责原则。
- 使用注解灵活配置mapper与实体。
代码中注释打的很清楚了,文字只简单描述一下。
源码链接(包括v1.0与v2.0): https://github.com/staticLin/customMyBatis.git
首先是SqlSession的改动,只改了构造器,将持有的executor变为动态新增的。
/**
* 用构造器将两个对象形成关系
*/
public CustomSqlSession(CustomConfiguration configuration) {
this.configuration = configuration;
//这里需要决定是否开启缓存,则从Configuraton中判断是否需要缓存,创建对应Executor
this.executor = configuration.newExecutor();
}
为了客户端方便调用,做了一个SqlSessionFactory负责接收信息初始化Configuration与创建SqlSession
/**
* @description: 创建SqlSession工厂
* @author: linyh
* @create: 2018-11-01 17:49
**/
public class SqlSessionFactory {
private CustomConfiguration configuration;
/**
* 以下build方法将初始化Factory的属性Configuration,具体工作在Configuration构造器中完成
*/
public SqlSessionFactory build(String mapperPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return this.build(mapperPath, null, false);
}
public SqlSessionFactory build(String mapperPath, String[] pluginPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return this.build(mapperPath, pluginPath, false);
}
public SqlSessionFactory build(String mapperPath, String[] pluginPath, boolean enableCache) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
configuration = new CustomConfiguration(mapperPath, pluginPath, enableCache);
return this;
}
/**
* 根据配置信息(Configuration)获取对应的SqlSession
*/
public CustomSqlSession openSqlSession(){
return new CustomSqlSession(configuration);
}
}
然后是Configuration,值得一提的是新增扫描包下注解获取运行时信息功能,新增了插件功能,这里只做了对executor的拦截,所以新增一个创建executor方法,实现动态选择executor。
/**
* @description:
* @author: linyh
* @create: 2018-10-31 16:32
**/
public class CustomConfiguration {
public static final MapperRegistory mapperRegistory = new MapperRegistory();
public static final Map<String, String> mappedStatements = new HashMap<>();
private CustomInterceptorChain interceptorChain = new CustomInterceptorChain();
private boolean enableCache = false;
private List<Class<?>> mapperList = new ArrayList<>();
private List<String> classPaths = new ArrayList<>();
/**
* 初始化时Configuration加载所有Mapper信息、plugin信息、缓存是否开启信息
*/
public CustomConfiguration(String mapperPath, String[] pluginPath, boolean enableCache) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//扫描mapper路径,将必要的mapper信息存入mapperRegistory与mapperStatements
scanPackage(mapperPath);
for (Class<?> mapper : mapperList) {
//当类为接口时视其为mapper,开始解析它
//Myabtis中判断是否为mapper还用到了isIndependent的方法判断,较为复杂,这里简化,体现思想即可
if (mapper.isInterface()) {
parsingClass(mapper);
}
}
if (pluginPath != null) {
//遍历plugin路径,初始化plugin并放入list中
for (String plugin : pluginPath) {
Interceptor interceptor = (Interceptor) Class.forName(plugin).newInstance();
interceptorChain.addInterceptor(interceptor);
}
}
//设置缓存是否开启
this.enableCache = enableCache;
}
/**
* MapperProxy根据statementName查找是否有对应SQL
*/
public boolean hasStatement(String statementName) {
return mappedStatements.containsKey(statementName);
}
/**
* MapperProxy根据statementID获取SQL
*/
public String getMappedStatement(String id) {
return mappedStatements.get(id);
}
public <T> T getMapper(Class<T> clazz, CustomSqlSession sqlSession) {
return mapperRegistory.getMapper(clazz, sqlSession);
}
/**
* 创建一个Executor(因为加入了plugin功能,需要判断是否创建带plugin的executor)
*/
public CustomExecutor newExecutor() {
CustomExecutor executor = createExecutor();
if (interceptorChain.hasPlugin()) {
return (CustomExecutor)interceptorChain.pluginAll(executor);
}
return executor;
}
/**
* 创建一个Executor(需要判断是否创建带缓存功能的executor)
*/
private CustomExecutor createExecutor(){
if (enableCache) {