面试官你好,我自己手写实现过Mybatis(超详细注释)

一、MyBatis核心组件

在开始实现我们的mybatis框架之前我觉得有必要先学习一下MyBatis核心组件,如下示意图(出自前文),在前面这个链接中可以了解到更多的细节。这里附上代码的github链接:github源码

在这里插入图片描述

二、MyBatis手写实现

1. 从测试用例作为入口

/**
 * 测试用例,将整个工程串联起来
 */
public class MybatisTest {
   
    public static void main(String[] args) throws IOException {
   
        //1.读取配置文件,将配置文件转换为输入流
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //返回DefaultSqlSessionFactory对象,利用XMLConfigBuilder类从输入流中读取配置信息。
        // 封装成Configuration对象传入DefaultSqlSessionFactory的构造方法
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂(DefaultSqlSessionFactory)生产SqlSession对象,
        SqlSession session = factory.openSession();
        //4.使用SqlSession创建Dao接口的代理对象
        IUserMapper mapper = session.getMapper(IUserMapper.class);
        //5.使用代理对象执行方法
        List<User> users = mapper.findAll();
        for (User user : users) {
   
            System.out.println(user);
        }
        //6.释放资源
        session.close();
        try {
   
            in.close();
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }
}

我们根据这个测试用例将整个mybatis执行sql过程分为几步去实现它:

  • SqlSessionFactoryBuilder:从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例,将Configuration对象传入。(配置信息封装成Configuration对象)。
  • SqlSessionFactory: 根据之前builder传入的封装好的Configuration可以创建SqlSession对象
    • Configuration: 将从XML配置文件中解析到的内容封装起来,其中包括数据源信息(数据库连接配置),还有一个sqlMappers(HashMap对象),其中key是由Mapper接口的全限定类名和方法名组成,value是一个SqlMapper对象。
    • SqlMapper: 存放的是要执行的SQL语句和要封装的实体类全限定类名。
  • SqlSession: 其中有要执行sql语句用的sqlMappers,并且持有数据库的连接。这些属性,都是在SqlSessionFactory创建SqlSession时传入的Configuration中的属性。它可以用于获取Mapper接口的增强代理对象(由jdk动态代理实现,非常关键的一步)。
  • MapperProxy: 实现了InvocationHandler接口,定义了代理对象的增强行为。他持有sqlMappers的引用,和数据库连接(SqlSession创建代理对象时传入)。然后拦截Mapper接口的所有方法,根据Method对象获取方法名和接口的全限名。然后拼接在一起,就是sqlmappers中需要的key,然后获取到maper对象,在使用数据库连接去数据库查询,并且封装返回数据。

    我认为上面这段分析远远比具体实现代码更加重要,如果有不懂可以查看代码后,反过来结合这段内容去分析代码,你会发现整个脉络分非常清晰。对动态代理不熟悉的可以查看我的另一篇文章两万字吐血总结,代理模式及手写实现动态代理(aop原理,基于jdk动态代理),动态代理在所有的框架中如spring、mybatis都扮演着十分重要的角色。

2.整个项目结构

在这里插入图片描述

3. 实现代码

可以打开右边的目录栏来快速跳转

(1)、Resources
/**
 * 使用类加载器读取配置文件的类
 */
public class Resources {
   
    public static InputStream getResourceAsStream(String filePath) {
   
        return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }
}

(2)、SqlSessionFactoryBuilder
/**
 * 根据输入流构建SqlSessionFactory对象
 */
public class SqlSessionFactoryBuilder {
   
    public SqlSessionFactory build(InputStream inputStream) {
   
        return new DefaultSqlSessionFactory(
                XMLConfigBuilder.loadConfiguration(inputStream)
        );
    }
}
(3)、SqlSessionFactory
/**
 * 创建SqlSession对象的工厂类
 */
public interface SqlSessionFactory {
   
    SqlSession openSession();
}


/**
 * 根据传入的Configuration创建SqlSession
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory {
   

    private Configuration cfg;

    @Override
    public SqlSession openSession() {
   
        return new DefaultSqlSession(cfg.getMappers(), DataSourceUtil.getConnection(cfg));
    }

    public DefaultSqlSessionFactory(Configuration cfg) {
   
        this.cfg = cfg;
    }
}

(4)、SqlSession
/**
 * 自定义Mybatis中和数据库交互的核心类,可以创建dao包中Mapper接口的代理对象
 */
public interface SqlSession {
   
    /**
     * 通过mapper字节码获取代理对象
     *
     * @param mapperClass 被代理对象字节码对象
     * @param <T>         被代理类型
     * @return 代理对象
     */
    <T> T getMapper(Class<T> mapperClass);

    /**
     * 释放资源
     */
    void close();
}

/**
 * 获取Mapper代理对象执行查询
 */
public class DefaultSqlSession implements SqlSession {
   


   private Map<String, SqlMapper> sqlMappers;

    private Connection connection;

    public DefaultSqlSession(Map<String, SqlMapper> sqlMappers, Connection connection) {
   
        this.sqlMappers = sqlMappers;
        this.connection = connection;
    }

    @Override
    public <T> T getMapper(Class<T> mapperClass) {
   
        return (T) Proxy.newProxyInstance(mapperClass.getClassLoader(),
                new Class[]{
   mapperClass},
                new MapperProxy(sqlMappers, connection));
    }

    @Override
    public void close() {
   
        if (connection != null) {
   
            try {
   
                connection.close();
            } catch (Exception e) {
   
                e.printStackTrace();
            }
        }
    }
}
(5)、MapperProxy
/**
 * 增强Mapper,生成代理对象
 */
public class MapperProxy implements InvocationHandler {
   

    private Map<S
评论 66
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值