为什么就能直接调用userMapper接口的方法?
测试代码:
package org.apache.ibatis.zytest;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
/**
* <p>Desription: </p>
*
* @Author: 罗志远
* @Date: 2021/8/26 0:31
* @ProjectName: mybatis
* @Package: org.apache.ibatis.zytest
* @ClassName: MyTest
* @Version: 1.0
*/
public class MyTest {
private static SqlSessionFactory sqlSessionFactory;
@Test
public void test01() throws IOException {
//1、创建SqlSessionFactory
String resource = "org/apache/ibatis/zytest/mybatis-config.xml";
final Reader reader = Resources.getResourceAsReader(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
reader.close();
//2、获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3、获取mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//4、执行数据库操作,并处理结果集
User user = mapper.selectById("1", 25);
System.out.println(user);
}
}
重点代码:
//3、获取mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
问题1:getMapper返回的是个什么对象?
1.首先默认调用的是DefaultSqlSession的getMapper方法。
2.通过调用Configuration的getMapper方法。
3.继续调用MapperRegistry的getMapper方法。
// 已知的所有映射
// key:mapperInterface,即dao的数据库接口,不是方法
// value:MapperProxyFactory,即映射器代理工厂
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
这里就可以拿到动态代理对象工厂。
往MapperRegistry添加动态代理对象工厂的方法:addMapper
MapperProxyFactory对象里保存了mapper接口的class对象。
在MapperProxyFactory类中使用了两种设计模式:
1.单例模式methodCache(注册式单例模式)。
2.工厂模式getMapper()。
给映射接口类创建动态代理对象是在MapperProxyFactory实现。
问题2:为什么就可以调用他的方法?
getMapper方法返回的是一个JDK动态代理对象(类型是$Proxy+数字)。这个代理对象会继承Proxy类,实现被代理的接口UserMpper,里面持有了一个MapperProxy类型的触发管理类。
当我们调用UserMpper的方法时候,实质上调用的是MapperProxy的invoke方法。
为什么要在MapperRegistry中保存一个工厂类?
原来他是用来创建并返回代理类的。这里是代理模式的一个非常经典的应用。
MapperProxy如何实现对接口的代理?
JDK动态代理
我们知道,JDK动态代理有三个核心角色:
被代理类(即就是实现类)
接口
实现了InvocationHanndler的触发管理类,用来生成代理对象。
被代理类必须实现接口,因为要通过接口获取方法,而且代理类也要实现这个接口。
而Mybatis中并没有Mapper接口的实现类,怎么被代理呢?它忽略了实现类,直接对Mapper接口进行代理。
MyBatis动态代理:
在Mybatis中,JDK动态代理为什么不需要实现类呢?
这里我们的目的其实就是根据一个可以执行的方法,直接找到Mapper.xml中statement ID ,方便调用。
最后返回的userMapper就是MapperProxyFactory的创建的代理对象,然后这个对象中包含了MapperProxy对象,
问题3:到底是怎么根据Mapper.java找到Mapper.xml的?
最后我们调用userMapper.selectUserById(),本质上调用的是MapperProxy的invoke()方法。
如果根据(接口+方法名找到Statement ID ),这个逻辑在InvocationHandler子类(MapperProxy类)中就可以完成了,其实也就没有必要在用实现类了。
总结
本文中主要是讲getMapper方法,该方法实质上是获取一个JDK动态代理对象(类型是Proxy+数字),这个代理类会继承MapperProxy类,实现被代理的接口UserMapper,并且里面持有一个MapperProxy类型的触发管理类。这里我们就拿到代理类了,后面我们就可以使用这个代理对象进行方法调用。
问题涉及到的设计模式:
1.代理模式。
2.工厂模式。
3.单例模式。
整个流程图: