(动态代理) 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谁特别关心了你

标签列表

Copyright © 2018-2023 by 丶萧然丶
蜀ICP备20022082号-2