探秘MyBatis:手写Mapper代理的源码解析与实现

前言

基于前面写的文章:MyBatis精髓揭秘:Mapper代理实现的黑盒探索,里面详细的介绍了 MyBatis 代理的实现逻辑,整体来看就是基于 JDK 动态代理的实现,虽然我们在使用的时候没有创建任何的实现类,但是基于动态代理技术,我们可以无中生有。

本文我们就基于这个核心思想,手写一份超精简的 MyBatis 源码。

前提准备

准备数据库表并创建实体

/**
 * 账户实体
 *
 * @author 薛伟
 */
public class Account implements Serializable {

    private Integer id;

    private String name;

    private String password;

    public Account() {
    }

    public Account(Integer id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "Account{" + "id=" + id + ", name='" + name + '\'' + ", password='" + password + '\'' + '}';
    }
}

创建数据访问层

/**
 * 账户数据库访问
 *
 * @author 薛伟
 */
@Mapper
public interface AccountDao {

    /**
     * 查询全部
     */
    List<Account> getAll();
}

创建XML配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- 配置 namespace -->
<mapper namespace="world.xuewei.mybatis.dao.AccountDao">

    <select id="getAll" resultType="Account">
        select *
        from account;
    </select>
</mapper>

我们这里只准备了一个最简单的查询方法。

创建测试类

public class DaoTest {

    private SqlSession sqlSession;

    @Before
    public void before() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        sqlSession = sessionFactory.openSession();
    }

    @After
    public void after() {
        sqlSession.commit();
    }
	
    // 这里编写测试类
}

代码实现

首先我们可以基于原生的 ibatis 和 MyBatis 来调用上面创建的 AccountDao.getAll 方法实现查询效果。

/**
  * 测试原生 iBatis 
  */
@Test
public void sqlSessionTest() {
    List<Account> list = sqlSession.selectList("world.xuewei.mybatis.dao.AccountDao.getAll");
    System.out.println(list);
}

/**
  * 测试原生 MyBatis 
  */
@Test
public void testGetAll() {
    AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
    System.out.println(accountDao.getAll());
}

接下来就是代理的核心实现

/**
  * 测试自定义代理实现
  */
@Test
public void proxyTest() {
    ClassLoader classLoader = this.getClass().getClassLoader();
    Class<?>[] interfaces = new Class[]{AccountDao.class};
    MyMapperProxy handler = new MyMapperProxy(sqlSession, AccountDao.class);
    AccountDao dao = (AccountDao) Proxy.newProxyInstance(classLoader, interfaces, handler);
    System.out.println(dao.getAll());
}

上面的代码我们为 AccountDao 创建了代理对象,调用方法的增强逻辑交个 去处理。初始化 MyMapperProxy 时将当前的 SqlSession 对象和要 Mapper 的类型传递过去。接下来我们看一下 MyMapperProxy 是如何实现的。

/**
 * Mapper 代理增强
 *
 * @author XUEW
 */
public class MyMapperProxy implements InvocationHandler {

    private final SqlSession sqlSession;

    private final Class<?> daoClass;

    public MyMapperProxy(SqlSession sqlSession, Class<?> daoClass) {
        this.sqlSession = sqlSession;
        this.daoClass = daoClass;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return sqlSession.selectList(daoClass.getName() + "." + method.getName(), args);
    }
}

我们这个示例只有一个查询方法,并且没有参数,所以这里的 invoke 方法是非常简单的。直接就调用了 sqlSession 的查询方法。

尽管代码相当的简单,但是 MyBatis 关于代理的核心实现就是这样的,只不过他设计的更精美,且考虑问题更加全面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值