前言
怎么说呢,这是个不算大也不是很核心的知识点,认真思考了几秒钟,还是单独介绍一下比较好。
下面这段代码是我们平时使用Mybatis时的一个常见场景,而ImageInfoMapper是一个接口,众所周知,java执行一个非静态方法,一般需要一个对象实体,毕竟java是面向对象的,所以问题就来了,ImageInfoMapper的实体是怎么创建出来的呢?
public void select() {
ImageInfo info = imageInfoMapper.byMd5("6e705a7733ac5gbwopmp02");
log.info(">>>>>>>>>>>>>>>>>>>=>,{}", info);
}
时序图
源码
从时序图中可以看出,Mapper接口的java对象是通过动态代理生成的,关键代码在MapperRegistry类中,如下:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 这里,通过factory生成代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
当然,核心任然是我们熟悉的Java内置Proxy类。
// 对外暴露的方法
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 核心是通过java的Proxy类生成动态代理
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
小细节
在前言中看到的例子,我们还可以通过代码创建sqlSession的方式来调用,执行效果是一样的。区别在于getMapper方法,这里每次调用都会创建一个代理对象,而前言中通过注入的方式获取imageInfoMapper代理对象是通用的,单例模式下,全局唯一,不会重复创建代理对象,所以我们一般不直接使用sqlSession。
@Test
public void sqlSession() {
SqlSession sqlSession = sqlSessionFactory.openSession();
ImageInfoMapper mapper = sqlSession.getMapper(ImageInfoMapper.class);
ImageInfo info = mapper.byMd5("6e705a7733ac5gbwopmp02");
log.info(">>>>>>>>>>>>>>>>>>>=>,{}", info);
}
sqlSession中getMapper方法调用路线,与注入的方式也有略微差别,不过疏通同归,最终都是通过MapperProxyFactory获取代理对象。
- sqlSession.getMapper()
- Configuration.getMapper()
- MapperRegistry.getMapper()
- MapperProxyFactory.newInstance()