(动态代理) mybatis-plus BaseMapper 模拟实现
前言
最近闲来无事,阅读了一下 mybatis-plus 的源码,好奇于为什么 mybatis-plus 的使用我们只需要 写一个接口继承至 它提供的 BaseMapper 接口就可以直接使用其内的方法,而无需写实现类,下面是自己模拟的过程,可能阅读得不是很深,下面只是自己的想法,可能有些许错误。
源码打包地址:https://gitee.com/xiao-ran-lzx/mybatis-plus-test001
BaseMapper
/**
* @author xiaoRan
*/
public interface BaseMapper<T> {
/**
* 通过用户id获取用户对象
* @param id 主键id
* @return 用户对象
*/
T getById(Integer id);
}
模拟 mybatis-plus 的 BaseMapper ,这里我就写了一个接口用来测试
User
/**
* @author xiaoRan
*/
public class User {
private Integer userId;
private String userName;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
用户实体类
UserMapper
/**
* @author xiaoRan
*/
public interface UserMapper extends BaseMapper<User>{
}
UserMapper 什么都没有
UserService
/**
* @author xiaoRan
*/
public interface UserService {
User getUserById(Integer id);
}
UserServiceImpl
import java.lang.reflect.Proxy;
/**
* @author xiaoRan
*/
public class UserServiceImpl implements UserService {
private UserMapper userMapper = getUserMapper();
@Override
public User getUserById(Integer id) {
return userMapper.getById(id);
}
/**
* 获取 userMapper 代理对象
* @return userMapper
*/
public UserMapper getUserMapper(){
UserInvocationHandler userInvocationHandler = new UserInvocationHandler();
return (UserMapper) Proxy.newProxyInstance(UserMapper.class.getClassLoader(), new Class<?>[]{UserMapper.class}, userInvocationHandler);
}
}
业务接口的实现类中,之前我们的 mapper 是通过 spring 的 autowired 自动注入的,这里我们生成代理对象的方式来获取。这里是通过 java.lang.reflect.Proxy包下的newProxyInstance来创建的代理对象,可以自行百度看看很简单
UserInvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author xiaoRan
*/
public class UserInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(Object.class.equals(method.getDeclaringClass())){
// 已实现的具体类
return method.invoke(this, args);
} else {
Object rst = null;
// 接口
if ("getById".equals(method.getName())) {
SqlSession sqlSession = new SqlSessionTemplate();
rst = sqlSession.getById((Integer) args[0]);
}
return rst;
}
}
}
代理对象的调用处理程序。这里我们先判断我们代理的类是否是一个具体的类(实现类),是的话我们就直接 调用目标类的方法就行了,否则如果不是的话,那就应该是接口,是没有实现类的,我们就需要自己处理这个放方法,这个写法我也是从 mybatis-plus 的源码中发现的,如下图是mybatis-plus源码的写法:
SqlSession
/**
* @author xiaoRan
*/
public interface SqlSession {
<T> T getById(Integer id);
}
这个接口是 跟 baseMapper相差无异的 一个接口,后面解释为什么要这个
SqlSessionTemplate
/**
* @author xiaoRan
*/
public class SqlSessionTemplate implements SqlSession{
@Override
public <T> T getById(Integer id) {
System.out.println("从数据库获取到了 id 为"+id+", 的对象");
return null;
}
}
这个类是 sqlsession 的一个实现类,真正执行数据库操作的类
Main
/**
* @author xiaoRan
*/
public class Main {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.getUserById(666);
}
}
这里直接测试,测试结果如下:
这里说一下自己看 mybatis-plus 后发现的执行过程,首先我们调用 mapper 接口中的方法,他会根据方法的返回值类型,方法名称等等方法的基本信息来判断是 增删改查的哪一种,然后分开来执行,比如属于是 selectList,那么就会调用 sqlSession 接口中对应的方法,也就是说 BaseMapper 后面其实有一个跟 BaseMapper 接口差不多的 接口叫 SqlSession ,这个接口是有实现类的,还有很多其他的方法和拓展的功能,我们调用的 baseMapper 中的接口都会被反射的方式调用到 sqlsession 中的方法(实现类 sqlSessionTemplate中的方法),在其中真正的执行了数据库的系列查询操作。
感兴趣可以自己去看看 mybatis-plus 的源码
从 MybatisMapperProxy 的 invoke 方法一步步看下去就行。
文章推荐
-
html移动端页面滑动卡顿
-
springboot连接root数据库失败
-
前端图片下载功能实现
-
记一次vue项目添加富文本编辑框出现的某些bug
-
项目引入阿里矢量库图标
-
【绝技】查看QQ谁特别关心了你