1.一个核心问题
为什么使用mapper接口就能操作数据库?(mapper接口并没有实现类)
Ans:配置文件解读+动态代理的增强
动态代理增强就是binding模块的功能。
从表现来讲,bingding的主要功能是将面向mapper接口编程转换成session中对应的方法执行。
2.binding模块分析
- MapperRegistry:mapper接口和对应的代理对象工厂的注册中心;
MapperProxyFactory:用于生成mapper接口动态代理的实例对象;
MapperProxy:实现了InvocationHandler接口,它是增强mapper接口的实现;
MapperMethod:封装了Mapper接口中对应方法的信息,以及对应的sql语句的信息,是mapper接口与映射配置文件中的sql语句的桥梁。
3.流程分析
@Test
// 测试自动映射以及下划线自动转化驼峰
public void quickStart() throws IOException {
// 2.获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.获取对应mapper
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
// 4.执行查询语句并返回结果
TUser user = mapper.selectByPrimaryKey(1);
System.out.println(user.toString());
}
3.1 创建动态代理对象流程
// 3.获取对应mapper
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
step1.SqlSession的唯一实现是DefaultSqlSession,getMapper方法如下:
@Override
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
step2.调用Configuration的getMapper方法:
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
其中mapperRegistry如下:
/*mapper接口的动态代理注册中心*/
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
step3.调用MapperRegistry的getMapper方法:
@SuppressWarnings("unchecked")
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
step4.调用MapperProxyFactory的newInstance:
public T newInstance(SqlSession sqlSession) {
//每次调用都会创建新的MapperProxy对象
final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
调用另一个newInstance,动态代理的标志性语句Proxy.newProxyInstance,最后一个是InvocationHandler——mapperProxy。
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
//创建实现了mapper接口的动态代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
step5.动态代理最终要调用的InvocationHandler——mapperProxy
public class MapperProxy implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;//记录关联的sqlsession对象
private final Class mapperInterface;//mapper接口对应的class对象;
//key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享
private final Map methodCache;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增强
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
//调用execute方法执行sql
return mapperMethod.execute(sqlSession, args);
}
3.2 执行查询流程
// 4.执行查询语句并返回结果
TUser user = mapper.selectByPrimaryKey(1);
step1.调用invoke:
public class MapperProxy implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;//记录关联的sqlsession对象
private final Class mapperInterface;//mapper接口对应的class对象;
//key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享
private final Map methodCache;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增强
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
//调用execute方法执行sql
return mapperMethod.execute(sqlSession, args);
}
step2.获取MapperMethod对象
MapperMethod封装了Mapper接口中对应方法的信息,以及对应的sql语句的信息,它是mapper接口与映射配置文件中sql语句的桥梁,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享。
- SqlCommand:从configuration中获取方法的命名空间、方法名以及SQL语句的类型;
MethodSignature:封装mapper接口方法的相关信息(入参、返回类型);
ParamNameResolver:解析mapper接口方法中的入参。
//从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
public class MapperMethod {
//从configuration中获取方法的命名空间.方法名以及SQL语句的类型
private final SqlCommand command;
//封装mapper接口方法的相关信息(入参,返回类型);
private final MethodSignature method;
public MapperMethod(Class> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
step2-1.获取SqlCommand
public SqlCommand(Configuration configuration, Class> mapperInterface, Method method) {
final String methodName = method.getName();//获取方法名称
final Class> declaringClass = method.getDeclaringClass();
//从configuration中获取mappedStatement
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if(method.getAnnotation(Flush.class) != null){
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {//如果mappedStatement不为空
name = ms.getId();//获取sql的名称,命名空间+方法名称
type = ms.getSqlCommandType();//获取sql语句的类型
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
其中核心是:
//从configuration中获取mappedStatement
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
step2-2.获取MethodSignature
public MethodSignature(Configuration configuration, Class> mapperInterface, Method method) {
//通过类型解析器获取方法的返回值类型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class>) {
this.returnType = (Class>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
//初始化返回值等字段
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Jdk.optionalExists && Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
step3.调用MapperMethod.execute执行sql
//调用execute方法执行sql
return mapperMethod.execute(sqlSession, args);
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//根据sql语句类型以及接口返回的参数选择调用不同的
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {//返回值为void
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {//返回值为集合或者数组
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {//返回值为map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {//返回值为游标
result = executeForCursor(sqlSession, args);
} else {//处理返回为单一对象的情况
//通过参数解析器解析解析参数
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = OptionalUtil.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
最后执行的是sqlSession.selectOne:
//处理返回为单一对象的情况
//通过参数解析器解析解析参数
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = OptionalUtil.ofNullable(result);
}
4.总结
step1.通过动态代理创建了代理对象MapperProxy
step2.MapperProxy最后调用的是MapperMethod的方法执行
最后的核心落在了MapperMethod,其封装了Mapper接口中对应方法的信息,以及对应的sql语句的信息,它是mapper接口与映射配置文件中sql语句的桥梁。
参考
- 1)享学课堂Lison老师笔记