MyBatis中Mapper的动态代理模式
0 疑问
在 Mybatis 中,我们只需要定义 ⌈Mapper接口⌋ 和 ⌈该Mapper接口所对应的XML配置文件⌋,就可以完成对数据库的 “增删查改” 操作,获取结果集。但是,从始至终我们都没有定义 Mapper 接口的实现类,且 Java 中也不允许使用接口来创建实例对象,那么当我们来调用接口中的方法时,到底是由谁来执行这个方法呢?
- 答案就是 Mybatis 借助设计模式中的 ⌈动态代理模式⌋ 帮助我们在底层创建了 Mapper 接口的实现类,从而调用该实现类的方法完成了我们所需要的操作;
- 对设计模式中的 ⌈代理模式⌋ 不熟悉的小伙伴可以看我的另一篇 优快云 博客:设计模式之代理模式;
- 在此过程中,主要涉及三个至关重要的类:MapperRegistry、MapperProxy 和 MapperMethod。
1 示例
我们以下面的代码为例,深入源码且言简意赅地说明 Mybatis 中的 Mapper 动态代理的实现过程,其中重点是第 5、6 点。
@Test
public void test() throws IOException {
//1.读取MyBatis的核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//3.通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//4.创建SqlSession对象,此时通过SqlSession对象所操作的sql都会自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//5.通过动态代理模式创建UserMapper接口的代理实现类对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//6.调用UserMapper接口中的方法
User user = userMapper.getUserById();
System.out.println(user.toString());
}
2 实现过程
2.1 代理对象的生成
- 当执行到示例代码的第 5 点时,Mybatis 在底层借助 MapperRegistry 类生成 UserMapper 接口的具体实现类,即 userMapper 对象的代理对象。下面是 MapperRegistry 类的源码,其中最重要的是
getMapper()
方法,下面对其进行详细解释;
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
public MapperRegistry(Configuration config) {
this.config = config;
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {