在使用mybatis的时候需要使用对应的接口,不像需要写具体的实现,就可以实现数据库的操作,Mybatis可以根据接口和对应的方法找到xml文件中sql语句,进行绑定,以及参数的填充。接下来我们看下binding模块的代码。下面是核心组件的关系
- MapperProxy:是Mapper接口的代理对象,让我们在使用mybatis的时候不需要实现接口
- MapperProxyFactory:MapperProxy的工厂类
MapperProxy
实现了InvocationHandler接口,通过代理的方式为Mapper接口实现代理对象。
//代理对象执行方法,代理以后,所有Mapper的方法调用的时候,都对调用这个方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//通过是Object的通用方法,无须执行
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
//如果是默认的方法则执行默认方法
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//从缓存中获取一个MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行对应的方法
return mapperMethod.execute(sqlSession, args);
}
主要调用的是MapperMethod的方法
MapperProxyFactory
MapperProxyFactory就是用来创建MapperProxxy类的
//代理类的创建
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
MapperRegistry
在mybatis初始化的时候,加载配置文件及mapper接口信息,注册在MapperRegistry中,需要执行sql语句的时候,在从注册中心获取mapper接口的代理对象。
注册mapper接口
public <T> void addMapper(Class<T> type) {
//判断是不是接口
if (type.isInterface()) {
//避免重复注册
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//为每一个mapper接口创建一个代理工厂MapperProxyFactory
knownMappers.put(type, new MapperProxyFactory<>(type));
//xml的解析和注解的处理
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
获取接口代理
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 {
//通过工厂创建代理MapperProxy
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperMethod
MapperProxy代理中是通过调用MapperMethod执行的,其主要完成方法参数的转换和执行方法对应的SQL。可以看做是Mapper接口方法与SQL语句之间的桥梁,只有两个属性SqlCommand和MethodSignature。核心的方法是execute,执行对应的sql语句。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
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()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
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 = Optional.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;
}
SqlCommand
记录了 SQL 语句的名称和类型
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
//SQL语句的名称有接口名+方法构成的,MappedStatement封装了SQL语句的基本信息
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 {
//获取 SQL 名称和类型
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
MethodSignature
接口中方法对应的信息,内部使用了ParamNameResolver解析参数