基于上一篇博客,手写MyBatis1.0末尾提出的几个不足之处与需要新增的地方,这篇博客将完善之前的MyBatis1.0版本,升级为2.0版本~将会新增的功能:
- 加入Plugin插件功能。
- 加入缓存功能。
- 分解Executor指责,分出各个类使其符合单一职责原则。
- 使用注解灵活配置mapper与实体。
代码中注释打的很清楚了,文字只简单描述一下。
**源码链接(包括v1.0与v2.0): **
首先是SqlSession的改动,只改了构造器,将持有的executor变为动态新增的。
12345678
/** * 用构造器将两个对象形成关系 */ public CustomSqlSession(CustomConfiguration configuration) { this.configuration = configuration; //这里需要决定是否开启缓存,则从Configuraton中判断是否需要缓存,创建对应Executor this.executor = configuration.newExecutor(); }
/**
* 用构造器将两个对象形成关系
*/
public CustomSqlSession(CustomConfiguration configuration) {
this.configuration = configuration;
//这里需要决定是否开启缓存,则从Configuraton中判断是否需要缓存,创建对应Executor
this.executor = configuration.newExecutor();
}
为了客户端方便调用,做了一个SqlSessionFactory负责接收信息初始化Configuration与创建SqlSession
1234567891011121314151617181920212223242526272829303132
/** * @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); }}
/**
- @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。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
/** * @description: * @author: linyh * @create: 2018-10-31 16:32 **/public class CustomConfiguration { public static final MapperRegistory mapperRegistory = new MapperRegistory(); public static final MapString, String mappedStatements = new HashMap(); private CustomInterceptorChain interceptorChain = new CustomInterceptorChain(); private boolean enableCache = false; private ListClass? mapperList = new ArrayList(); private ListString 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(ClassT 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) { return new CacheExecutor(new SimpleExecutor()); } return new SimpleExecutor(); } /** * 解析类中的注解 */ private void parsingClass(Class? mapper) { //如有Pojo注解,则可以获取到实体类的信息 if (mapper.isAnnotationPresent(Pojo.class)) { for (Annotation annotation : mapper.getAnnotations()) { if (annotation.annotationType().equals(Pojo.class)) { //将mapper与实体类信息注册进mapperRegistory中 mapperRegistory.addMapper(mapper, ((Pojo)annotation).value()); } } } Method[] methods = mapper.getMethods(); for (Method method : methods) { //TODO 新增Update、Delete、Insert注解 //如果有Select注解就解析SQL语句 if (method.isAnnotationPresent(Select.class)) { for (Annotation annotation : method.getDeclaredAnnotations()) { if (annotation.annotationType().equals(Select.class)) { //将方法名与SQL语句注册进mappedStatements中 mappedStatements.put(method.getDeclaringClass().getName() + "." +method.getName(), ((Select) annotation).value()); } } } } } /** * 扫描包名,获取包下的所有.class文件 */ private void scanPackage(String mapperPath) throws ClassNotFoundException { String classPath = TestMybatis.class.getResource("/").getPath(); mapperPath = mapperPath.replace(".", File.separator); String mainPath = classPath + mapperPath; doPath(new File(mainPath)); for (String className : classPaths) { className = className.replace(classPath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class",""); Class? clazz = Class.forName(className); mapperList.add(clazz); } } /** * 该方法会得到所有的类,将类的绝对路径写入到classPaths中 */ private void doPath(File file) { if (file.isDirectory()) {//文件夹 //文件夹我们就递归 File[] files = file.listFiles(); for (File f1 : files) { doPath(f1); } } else {//标准文件 //标准文件我们就判断是否是class文件 if (file.getName().endsWith(".class")) { //如果是class文件我们就放入我们的集合中。 classPaths.add(file.getPath()); } } }}
/**
- @description:
- @author: linyh
- @create: 2018-10-31 16:32
**/
public class CustomConfiguration {
public static final MapperRegistory mapperRegistory = new MapperRegistory();
public static final MapString, String mappedStatements = new HashMap();
private CustomInterceptorChain interceptorChain = new CustomInterceptorChain();
private boolean enableCache = false;
private ListClass? mapperList = new ArrayList();
private ListString 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 getMapper(Class 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) {
return new CacheExecutor(new SimpleExecutor());
}
return new SimpleExecutor();
}
/**
* 解析类中的注解
*/
private void parsingClass(Class? mapper) {
//如有Pojo注解,则可以获取到实体类的信息
if (mapper.isAnnotationPresent(Pojo.class)) {
for (Annotation annotation : mapper.getAnnotations()) {
if (annotation.annotationType().equals(Pojo.class)) {
//将mapper与实体类信息注册进mapperRegistory中
mapperRegistory.addMapper(mapper, ((Pojo)annotation).value());
}
}
}
Method[] methods = mapper.getMethods();
for (Method method : methods) {
//TODO 新增Update、Delete、Insert注解
//如果有Select注解就解析SQL语句
if (method.isAnnotationPresent(Select.class)) {
for (Annotation annotation : method.getDeclaredAnnotations()) {
if (annotation.annotationType().equals(Select.class)) {
//将方法名与SQL语句注册进mappedStatements中
mappedStatements.put(method.getDeclaringClass().getName() + "." +method.getName(),
((Select) annotation).value());
}
}
}
}
}
/**
* 扫描包名,获取包下的所有.class文件
*/
private void scanPackage(String mapperPath) throws ClassNotFoundException {
String classPath = TestMybatis.class.getResource("/").getPath();
mapperPath = mapperPath.replace(".", File.separator);
String mainPath = classPath + mapperPath;
doPath(new File(mainPath));
for (String className : classPaths) {
className = className.replace(classPath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");
Class? clazz = Class.forName(className);
mapperList.add(clazz);
}
}
/**
* 该方法会得到所有的类,将类的绝对路径写入到classPaths中
*/
private void doPath(File file) {
if (file.isDirectory()) {//文件夹
//文件夹我们就递归
File[] files = file.listFiles();
for (File f1 : files) {
doPath(f1);
}
} else {//标准文件
//标准文件我们就判断是否是class文件
if (file.getName().endsWith(".class")) {
//如果是class文件我们就放入我们的集合中。
classPaths.add(file.getPath());
}
}
}
}
然后是三个简单的注解创建。
1234567891011121314151617181920212223242526272829303132333435
/** * @Author:linyh * @Date: 2018/11/2 10:38 * @Modified By: */@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Method { String value();} /** * @Author:linyh * @Date: 2018/11/1 17:40 * @Modified By: */@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Pojo { Class? value();} /** * @Author:linyh * @Date: 2018/11/1 17:37 * @Modified By: */@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Select { String value();}
/**
- @Author:linyh
- @Date: 2018/11/2 10:38
- @Modified By:
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Method {
String value();
}
/**
- @Author:linyh
- @Date: 2018/11/1 17:40
- @Modified By:
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Pojo {
Class? value();
}
/**
- @Author:linyh
- @Date: 2018/11/1 17:37
- @Modified By:
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
String value();
}
在mapper中这样使用,为了解析到SQL语句与mapper对应的实体类。
1234567891011
/** * @Author:linyh * @Date: 2018/10/31 16:56 * @Modified By: */@Pojo(Test.class)public interface TestCustomMapper { @Select("select * from test where id = %d") Test selectByPrimaryKey(int id);}
/**
- @Author:linyh
- @Date: 2018/10/31 16:56
- @Modified By:
*/
@Pojo(Test.class)
public interface TestCustomMapper {
@Select("select * from test where id = %d")
Test selectByPrimaryKey(int id);
}
然后是之前的默认executor,细化了职责。
123456789101112131415
/** * @description: 自定义Executor * @author: linyh * @create: 2018-10-31 17:46 **/public class SimpleExecutor implements CustomExecutor { /** * 这里为默认Executor,细化了责任,查询交给StatementHandler处理 */ @Override public T T query(String statement, String parameter, Class pojo) throws IllegalAccessException, SQLException, InstantiationException, InvocationTargetException, NoSuchMethodException { StatementHandler statementHandler = new StatementHandler(); return statementHandler.query(statement, parameter, pojo); }}
/**
- @description: 自定义Executor
- @author: linyh
- @create: 2018-10-31 17:46
/
public class SimpleExecutor implements CustomExecutor {
/
* 这里为默认Executor,细化了责任,查询交给StatementHandler处理
*/
@Override
public T query(String statement, String parameter, Class pojo) throws IllegalAccessException, SQLException, InstantiationException, InvocationTargetException, NoSuchMethodException {
StatementHandler statementHandler = new StatementHandler();
return statementHandler.query(statement, parameter, pojo);
}
}
然后是使用了装饰器模式的在默认executor的功能之上新增缓存功能的executor
123456789101112131415161718192021222324252627282930313233343536373839404142434445
/** * @description: 缓存的Executor,用了装饰器模式 * @author: linyh * @create: 2018-11-01 18:02 **/public class CacheExecutor implements CustomExecutor{ //这里的delegate为被装饰的executor private CustomExecutor delegate; private static final MapInteger, Object cache = new HashMap(); /** * 装饰executor,为了在原有的executor之上增加新的功能,扩展为意图 */ public CacheExecutor(CustomExecutor delegate) { this.delegate = delegate; } /** * 修改了查询方法,扩展了缓存功能。 * 由于只实现了查询功能,所以这里的缓存处理没有清空的处理。 * 后续升级版本时需要新增缓存更新方法。 */ @Override public T T query(String statement, String parameter, Class pojo) throws IllegalAccessException, SQLException, InstantiationException, InvocationTargetException, NoSuchMethodException { //这里的CacheKey为缓存的关键 CacheKey cacheKey = new CacheKey(); //根据SQL语句与参数两个维度来判断每次查询是否相同 //底层原理为hashCode的计算,同一个SQL语句与同一个参数将视为同一个查询,cacheKey中的code也会一样 //在Mybatis中的cacheKey里的code维度有更多,这里简化体现思想即可 cacheKey.update(statement); cacheKey.update(parameter); //判断Map中是否含有根据SQL语句与参数算出来的code if (!cache.containsKey(cacheKey.getCode())) { //如没有就用被装饰者的查询方法,然后新增缓存 Object obj = delegate.query(statement, parameter, pojo); cache.put(cacheKey.getCode(), obj); return (T)obj; } //缓存命中 System.out.println("从缓存拿数据" + cache.get(cacheKey.getCode())); return (T)cache.get(cacheKey.getCode()); }}
/**
- @description: 缓存的Executor,用了装饰器模式
- @author: linyh
- @create: 2018-11-01 18:02
**/
public class CacheExecutor implements CustomExecutor{
//这里的delegate为被装饰的executor
private CustomExecutor delegate;
private static final MapInteger, Object cache = new HashMap();
/**
* 装饰executor,为了在原有的executor之上增加新的功能,扩展为意图
*/
public CacheExecutor(CustomExecutor delegate) {
this.delegate = delegate;
}
/**
* 修改了查询方法,扩展了缓存功能。
* 由于只实现了查询功能,所以这里的缓存处理没有清空的处理。
* 后续升级版本时需要新增缓存更新方法。
*/
@Override
public T query(String statement, String parameter, Class pojo) throws IllegalAccessException, SQLException, InstantiationException, InvocationTargetException, NoSuchMethodException {
//这里的CacheKey为缓存的关键
CacheKey cacheKey = new CacheKey();
//根据SQL语句与参数两个维度来判断每次查询是否相同
//底层原理为hashCode的计算,同一个SQL语句与同一个参数将视为同一个查询,cacheKey中的code也会一样
//在Mybatis中的cacheKey里的code维度有更多,这里简化体现思想即可
cacheKey.update(statement);
cacheKey.update(parameter);
//判断Map中是否含有根据SQL语句与参数算出来的code
if (!cache.containsKey(cacheKey.getCode())) {
//如没有就用被装饰者的查询方法,然后新增缓存
Object obj = delegate.query(statement, parameter, pojo);
cache.put(cacheKey.getCode(), obj);
return (T)obj;
}
//缓存命中
System.out.println("从缓存拿数据" + cache.get(cacheKey.getCode()));
return (T)cache.get(cacheKey.getCode());
}
}
顺便贴上缓存核心实现类(核心思想是把一次查询分为几个维度去计算hashCode,相同SQL语句与相同参数的每次查询算出来的Code将是相同的,即可视为同一查询)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
/** * @description: 缓存key值 * @author: linyh * @create: 2018-11-02 11:22 **/public class CacheKey { //沿用MyBatis设计的默认值 private static final int DEFAULT_HASHCODE = 17; private static final int DEFAULT_MULTIPLYER = 37; private int hashCode; private int count; private int multiplyer; public CacheKey() { this.hashCode = DEFAULT_HASHCODE; this.count = 0; this.multiplyer = DEFAULT_MULTIPLYER; } /** * 计算每一个传进来的Object的独有的一个code */ public void update(Object object){ if (object != null && object.getClass().isArray()) { int length = Array.getLength(object); for (int i = 0; i length; i++) { Object element = Array.get(object, i); doUpdate(element); } }else { doUpdate(object); } } public int getCode() { return hashCode; } private void doUpdate(Object o) { int baseHashCode = o == null ? 1 : o.hashCode(); count++; baseHashCode *= count; hashCode = multiplyer * hashCode + baseHashCode; }}
/**
- @description: 缓存key值
- @author: linyh
- @create: 2018-11-02 11:22
**/
public class CacheKey {
//沿用MyBatis设计的默认值
private static final int DEFAULT_HASHCODE = 17;
private static final int DEFAULT_MULTIPLYER = 37;
private int hashCode;
private int count;
private int multiplyer;
public CacheKey() {
this.hashCode = DEFAULT_HASHCODE;
this.count = 0;
this.multiplyer = DEFAULT_MULTIPLYER;
}
/**
* 计算每一个传进来的Object的独有的一个code
*/
public void update(Object object){
if (object != null && object.getClass().isArray()) {
int length = Array.getLength(object);
for (int i = 0; i length; i++) {
Object element = Array.get(object, i);
doUpdate(element);
}
}else {
doUpdate(object);
}
}
public int getCode() {
return hashCode;
}
private void doUpdate(Object o) {
int baseHashCode = o == null ? 1 : o.hashCode();
count++;
baseHashCode *= count;
hashCode = multiplyer * hashCode + baseHashCode;
}
}
然后是负责查询工作的类
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
/** * @description: 负责查询工作 * @author: linyh * @create: 2018-11-02 10:10 **/public class StatementHandler { private ResultSetHandler resultSetHandler = new ResultSetHandler(); /** * 主要执行查询工作的地方 */ public T T query(String statement, String parameter, Class pojo) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Connection conn = null; //TODO 这里需要一个ParameterHandler,然后用TypeHandler设置参数,偷懒没写TypeHandler... statement = String.format(statement, Integer.parseInt(parameter)); try { conn = getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(statement); preparedStatement.execute(); //查询到这里结束,映射结果的工作委派给ResultSetHandler去做 return resultSetHandler.handle(preparedStatement.getResultSet(), pojo); } finally { if (conn != null) { conn.close(); conn = null; } } } private Connection getConnection() throws SQLException { String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/gp?serverTimezone=UTC"; String username = "root"; String password = "admin"; Connection conn = null; try { Class.forName(driver); //classLoader,加载对应驱动 conn = DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return conn; }}
/**
- @description: 负责查询工作
- @author: linyh
- @create: 2018-11-02 10:10
**/
public class StatementHandler {
private ResultSetHandler resultSetHandler = new ResultSetHandler();
/**
* 主要执行查询工作的地方
*/
public T query(String statement, String parameter, Class pojo) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Connection conn = null;
//TODO 这里需要一个ParameterHandler,然后用TypeHandler设置参数,偷懒没写TypeHandler...
statement = String.format(statement, Integer.parseInt(parameter));
try {
conn = getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(statement);
preparedStatement.execute();
//查询到这里结束,映射结果的工作委派给ResultSetHandler去做
return resultSetHandler.handle(preparedStatement.getResultSet(), pojo);
} finally {
if (conn != null) {
conn.close();
conn = null;
}
}
}
private Connection getConnection() throws SQLException {
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://127.0.0.1:3306/gp?serverTimezone=UTC";
String username = "root";
String password = "admin";
Connection conn = null;
try {
Class.forName(driver); //classLoader,加载对应驱动
conn = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
}
负责映射结果集的类
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
/** * @description: 负责结果的映射 * @author: linyh * @create: 2018-11-02 10:10 **/public class ResultSetHandler { /** * 细化了executor的职责,专门负责结果映射 */ public T T handle(ResultSet resultSet, Class pojo) throws IllegalAccessException, InstantiationException, NoSuchMethodException, SQLException, InvocationTargetException { //代替了ObjectFactory Object pojoObj = pojo.newInstance(); //遍历pojo中每一个属性域去设置参数 if (resultSet.next()) { for (Field field : pojoObj.getClass().getDeclaredFields()) { setValue(pojoObj, field, resultSet); } } return (T)pojoObj; } /** * 利用反射给每一个属性设置参数 */ private void setValue(Object pojo, Field field, ResultSet rs) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, SQLException { Method setMethod = pojo.getClass().getMethod("set" + firstWordCapital(field.getName()), field.getType()); setMethod.invoke(pojo, getResult(rs, field)); } /** * 根据反射判断类型,从ResultSet中取对应类型参数 */ private Object getResult(ResultSet rs, Field field) throws SQLException { //TODO 这里需要用TypeHandle处理,偷懒简化了,后续升级点 Class type = field.getType(); if (Integer.class == type) { return rs.getInt(field.getName()); } if (String.class == type) { return rs.getString(field.getName()); } return rs.getString(field.getName()); } /** * 将一个单词首字母大写 */ private String firstWordCapital(String word){ String first = word.substring(0, 1); String tail = word.substring(1); return first.toUpperCase() + tail; }}
/**
- @description: 负责结果的映射
- @author: linyh
- @create: 2018-11-02 10:10
**/
public class ResultSetHandler {
/**
* 细化了executor的职责,专门负责结果映射
*/
public T handle(ResultSet resultSet, Class pojo) throws IllegalAccessException, InstantiationException, NoSuchMethodException, SQLException, InvocationTargetException {
//代替了ObjectFactory
Object pojoObj = pojo.newInstance();
//遍历pojo中每一个属性域去设置参数
if (resultSet.next()) {
for (Field field : pojoObj.getClass().getDeclaredFields()) {
setValue(pojoObj, field, resultSet);
}
}
return (T)pojoObj;
}
/**
* 利用反射给每一个属性设置参数
*/
private void setValue(Object pojo, Field field, ResultSet rs) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, SQLException {
Method setMethod = pojo.getClass().getMethod("set" + firstWordCapital(field.getName()), field.getType());
setMethod.invoke(pojo, getResult(rs, field));
}
/**
* 根据反射判断类型,从ResultSet中取对应类型参数
*/
private Object getResult(ResultSet rs, Field field) throws SQLException {
//TODO 这里需要用TypeHandle处理,偷懒简化了,后续升级点
Class type = field.getType();
if (Integer.class == type) {
return rs.getInt(field.getName());
}
if (String.class == type) {
return rs.getString(field.getName());
}
return rs.getString(field.getName());
}
/**
* 将一个单词首字母大写
*/
private String firstWordCapital(String word){
String first = word.substring(0, 1);
String tail = word.substring(1);
return first.toUpperCase() + tail;
}
}
其他的MapperProxy之类的没有什么改动,只是新增了一个实体类的属性,为了传递给结果集映射时使用。
最后介绍Plugin的实现~首先是保存所有plugin的类(此类将作为Configuration属性存放)。
12345678910111213141516171819202122232425262728293031
/** * @description: 插件链存放类 * @author: linyh * @create: 2018-11-01 17:59 **/public class CustomInterceptorChain { private final ListInterceptor interceptors = new ArrayList(); public void addInterceptor(Interceptor interceptor){ interceptors.add(interceptor); } /** * 将目标executor依此按照插件链动态代理target * 如有多个plugin,多次代理(如b插件代理了a执行器,c插件又代理a执行器,此时执行顺序为c - b - a) */ public Object pluginAll(Object target){ for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } public boolean hasPlugin(){ if (interceptors.size() == 0) { return false; } return true; }}
/**
- @description: 插件链存放类
- @author: linyh
- @create: 2018-11-01 17:59
**/
public class CustomInterceptorChain {
private final ListInterceptor interceptors = new ArrayList();
public void addInterceptor(Interceptor interceptor){
interceptors.add(interceptor);
}
/**
* 将目标executor依此按照插件链动态代理target
* 如有多个plugin,多次代理(如b插件代理了a执行器,c插件又代理a执行器,此时执行顺序为c - b - a)
*/
public Object pluginAll(Object target){
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public boolean hasPlugin(){
if (interceptors.size() == 0) {
return false;
}
return true;
}
}
插件的接口
12345678910
/** * @description: * @author: linyh * @create: 2018-11-02 10:12 **/public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target);}
/**
- @description:
- @author: linyh
- @create: 2018-11-02 10:12
**/
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
}
自定义插件实现上面的接口
12345678910111213141516171819202122232425262728
/** * @description: 测试用插件 * @author: linyh * @create: 2018-11-02 10:29 **/@Method("query")public class testPlugin implements Interceptor{ @Override public Object intercept(Invocation invocation) throws Throwable { String statement = (String) invocation.getArgs()[0]; String parameter = (String) invocation.getArgs()[1]; Class pojo = (Class) invocation.getArgs()[2]; System.out.println("----------plugin生效----------"); System.out.println("executor执行query方法前拦截,拦截到的Sql语句为: " + statement); System.out.println("参数为: " + parameter + " 实体类为: " + pojo.getName()); System.out.println("-----------拦截结束-----------"); //在这里执行原executor的方法 return invocation.proceed(); } /** * 实际上此方法就是将此插件给target做一个动态代理 */ @Override public Object plugin(Object target) { return Plugin.wrap(target, this); }}
/**
- @description: 测试用插件
- @author: linyh
- @create: 2018-11-02 10:29
**/
@Method(“query”)
public class testPlugin implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
String statement = (String) invocation.getArgs()[0];
String parameter = (String) invocation.getArgs()[1];
Class pojo = (Class) invocation.getArgs()[2];
System.out.println(“———-plugin生效———-“);
System.out.println(“executor执行query方法前拦截,拦截到的Sql语句为: “ + statement);
System.out.println(“参数为: “ + parameter + “ 实体类为: “ + pojo.getName());
System.out.println(“———–拦截结束———–”);
//在这里执行原executor的方法
return invocation.proceed();
}
/**
* 实际上此方法就是将此插件给target做一个动态代理
*/
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
一个执行原方法的类。
123456789101112131415161718192021222324252627282930313233343536
/** * @description: 此类为辅助plugin执行原方法的类,存放原executor、原方法、原参数 * @author: linyh * @create: 2018-11-02 10:13 **/public class Invocation { private Object target; private Method method; private Object[] args; public Invocation(Object target, Method method, Object[] args) { this.target = target; this.method = method; this.args = args; } public Object getTarget() { return target; } public Method getMethod() { return method; } public Object[] getArgs() { return args; } /** * 在这里执行原executor的方法 */ public Object proceed() throws InvocationTargetException, IllegalAccessException { return method.invoke(target, args); }}
/**
- @description: 此类为辅助plugin执行原方法的类,存放原executor、原方法、原参数
- @author: linyh
- @create: 2018-11-02 10:13
**/
public class Invocation {
private Object target;
private Method method;
private Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
/**
* 在这里执行原executor的方法
*/
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
然后是一个核心实现类,插件实际的代理者。
1234567891011121314151617181920212223242526272829303132333435363738394041424344
/** * @description: 插件代理者 * @author: linyh * @create: 2018-11-02 10:30 **/public class Plugin implements InvocationHandler { private Object target; private Interceptor interceptor; /** * @param target 被代理的Executor类 * @param interceptor plugin插件 */ public Plugin(Object target, Interceptor interceptor) { this.target = target; this.interceptor = interceptor; } /** * 包装executor的方法 * @param obj 被代理的Executor类 * @param interceptor plugin插件 * @return 代理类 */ public static Object wrap(Object obj, Interceptor interceptor) { Class clazz = obj.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new Plugin(obj, interceptor)); } /** * 代理方法核心 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判断插件上的注解是否与method的方法名匹配(需拦截executor的哪个一个方法) if (interceptor.getClass().isAnnotationPresent(com.test.mybatis.v2.annotation.Method.class)) { if (method.getName().equals(interceptor.getClass().getAnnotation(com.test.mybatis.v2.annotation.Method.class).value())) { //不执行原方法,直接执行插件方法 return interceptor.intercept(new Invocation(target, method, args)); } } return method.invoke(target, method, args); }}
/**
- @description: 插件代理者
- @author: linyh
- @create: 2018-11-02 10:30
**/
public class Plugin implements InvocationHandler {
private Object target;
private Interceptor interceptor;
/**
* @param target 被代理的Executor类
* @param interceptor plugin插件
*/
public Plugin(Object target, Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
/**
* 包装executor的方法
* @param obj 被代理的Executor类
* @param interceptor plugin插件
* @return 代理类
*/
public static Object wrap(Object obj, Interceptor interceptor) {
Class clazz = obj.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new Plugin(obj, interceptor));
}
/**
* 代理方法核心
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断插件上的注解是否与method的方法名匹配(需拦截executor的哪个一个方法)
if (interceptor.getClass().isAnnotationPresent(com.test.mybatis.v2.annotation.Method.class)) {
if (method.getName().equals(interceptor.getClass().getAnnotation(com.test.mybatis.v2.annotation.Method.class).value())) {
//不执行原方法,直接执行插件方法
return interceptor.intercept(new Invocation(target, method, args));
}
}
return method.invoke(target, method, args);
}
}
接下来编写测试类进行测试吧
12345678
SqlSessionFactory factory = new SqlSessionFactory(); //没有插件且没有开启缓存 CustomSqlSession sqlSession1 = factory.build("com.test.mybatis.v2.mapper").openSqlSession(); TestCustomMapper mapper1 = sqlSession1.getMapper(TestCustomMapper.class); Test test1 = mapper1.selectByPrimaryKey(1); System.out.println("第一次查询结果为: " + test1); test1 = mapper1.selectByPrimaryKey(1); System.out.println("第二次查询结果为: " + test1);
SqlSessionFactory factory = new SqlSessionFactory();
//没有插件且没有开启缓存
CustomSqlSession sqlSession1 = factory.build("com.test.mybatis.v2.mapper").openSqlSession();
TestCustomMapper mapper1 = sqlSession1.getMapper(TestCustomMapper.class);
Test test1 = mapper1.selectByPrimaryKey(1);
System.out.println("第一次查询结果为: " + test1);
test1 = mapper1.selectByPrimaryKey(1);
System.out.println("第二次查询结果为: " + test1);
控制台打印
第二个测试
123456789
SqlSessionFactory factory = new SqlSessionFactory(); //有插件但没有开启缓存 CustomSqlSession sqlSession2 = factory.build("com.test.mybatis.v2.mapper", new String[] {"com.test.mybatis.v2.plugin.customPlugin.testPlugin"}).openSqlSession(); TestCustomMapper mapper2 = sqlSession2.getMapper(TestCustomMapper.class); Test test2 = mapper2.selectByPrimaryKey(2); System.out.println("第一次查询结果为: " + test2); test2 = mapper2.selectByPrimaryKey(2); System.out.println("第二次查询结果为: " + test2);
SqlSessionFactory factory = new SqlSessionFactory();
//有插件但没有开启缓存
CustomSqlSession sqlSession2 = factory.build("com.test.mybatis.v2.mapper",
new String[] {"com.test.mybatis.v2.plugin.customPlugin.testPlugin"}).openSqlSession();
TestCustomMapper mapper2 = sqlSession2.getMapper(TestCustomMapper.class);
Test test2 = mapper2.selectByPrimaryKey(2);
System.out.println("第一次查询结果为: " + test2);
test2 = mapper2.selectByPrimaryKey(2);
System.out.println("第二次查询结果为: " + test2);
控制台打印
第三个测试
123456789
SqlSessionFactory factory = new SqlSessionFactory(); //有插件且有缓存 CustomSqlSession sqlSession3 = factory.build("com.test.mybatis.v2.mapper", new String[] {"com.test.mybatis.v2.plugin.customPlugin.testPlugin"}, true).openSqlSession(); TestCustomMapper mapper3 = sqlSession3.getMapper(TestCustomMapper.class); Test test3 = mapper3.selectByPrimaryKey(3); System.out.println("第一次查询结果为: " + test3); test3 = mapper3.selectByPrimaryKey(3); System.out.println("第二次查询结果为: " + test3);
SqlSessionFactory factory = new SqlSessionFactory();
//有插件且有缓存
CustomSqlSession sqlSession3 = factory.build("com.test.mybatis.v2.mapper",
new String[] {"com.test.mybatis.v2.plugin.customPlugin.testPlugin"}, true).openSqlSession();
TestCustomMapper mapper3 = sqlSession3.getMapper(TestCustomMapper.class);
Test test3 = mapper3.selectByPrimaryKey(3);
System.out.println("第一次查询结果为: " + test3);
test3 = mapper3.selectByPrimaryKey(3);
System.out.println("第二次查询结果为: " + test3);
控制台打印
其中plugin作用在executor的query方法之前,Mybatis不仅可以配置插件到executor的query中,还能配置插件到例如ResultSetHandler中。
以上图片为Mybatis官网中截取,可以看到拦截的方法可以有很多种,这里简化了一下,只拦截了executor的query方法,体会到Mybatis的思想即可。
到这里就大致完成了MyBatis2.0版本的编写了,依然存在很多不足之处,可见编写一个强大的框架是十分不易的,不可能一步登天就完成所有的功能,都是一点一点累加上去,正因为如此,一个好的设计理念,好的抽象思维就显得格外重要,需要架构者具有能设计出一个易扩展、符合面向对象设计原则的代码的能力,而这个能力实属不易,或许这就是架构师稀少的原因吧。
总结
不足之处与需要改进的地方
- 设置参数与结果映射都需要一个TypeHandler,这里偷懒简化了,可以作为下一个版本的加强点。
- SQL语句局限于查询方法,新增所有的增删改方法也是下个版本的加强点。
- 实现一个清空缓存的方法,在使用了增删改方法时有清空缓存的必要。
- Plugin插件功能扩展,使其可以作用到Executor以外的三个类,扩展可以拦截的方法。
以上提到的功能除了TypeHandler以外,都是在做重复工作,意义不大,所以我在2.0版本中没有去实现那些功能,例如增删改查方法我只实现一个查的方法, 作为一个例子,其他的增删改都是可以一样画葫芦的,Plugin功能扩展也是如此。
写到这里,越发觉得开发一个框架例如mybatis是多么的不易,感觉到mybatis底下组件的强大,2.0中一定还有很多没有提及的缺点,可以体会开发一个健壮的框架的难度了。经过这次自己手写MyBatis,更深刻的认识了MyBatis底层各种组件的实现原理,也巩固了一些设计模式、面向对象的一些原则。