Mybatis系列五:MapperRegistry注册并解析Mapper Class
概述
在《Mybatis系列一:构建SqlSessionFactory》一文中了解了Mybatis配置文件的解析,其中对mappers元素解析使用到了MapperRegistryr这个类,现在分析下这个类是如何解析Mapper Class的。当然也用到XMLMapperBuilder解析Mapper XML配置文件。
// XMLMapperBuilder解析Mapper.xml配置文件 // MapperAnnotationBuilder解析Mapper接口,调用XMLMapperBuilder解析Mapper.xml配置文件 **** ****Mapper.xml配置文件中的select|insert|update|delete元素解析成MappedStatement对象,保存Configuration#mappedStatements中。
Mapper.class注册到MapperRegistry#knownMappers中,映射一个MapperProxyFactory,为Mapper创建代理对象。
一、Configuration
1.1 回顾XMLConfigBuilder是如何解析mapper元素的
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if (“package”.equals(child.getName())) {
String mapperPackage = child.getStringAttribute(“name”);
configuration.addMappers(mapperPackage);
} else {
…
if (resource != null && url == null && mapperClass == null) {
…
} else if (resource == null && url != null && mapperClass == null) {
…
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
** configuration.addMapper(mapperInterface);**
} else {
…
}
}
}
}
}
1.2 扫描指定包下的Mapper Class
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
1.3 添加Mapper Class
public void addMapper(Class type) {
mapperRegistry.addMapper(type);
}
二、MapperRegistry
2.1 扫描指定包下的Mapper Class
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
2.2 扫描指定包下指定类型的Mapper Class
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil
2.3 添加Mapper Class
public void addMapper(Class type) {
if (type.isInterface()) {
// 是否已经注册
if (hasMapper(type)) {
throw new BindingException(“Type " + type + " is already known to the MapperRegistry.”);
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory(type));
// 必须在运行解析器之前//添加类型,否则//映射器解析器可能会自动尝试绑定。如果类型是已知的,则不会尝试。传送门《Mybatis系列六:MapperAnnotationBuilder解析Mapper Class》
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
2.4 获取Mapper接口代理实例
DefaultSqlSession#getMapper调用的是Configuration#getMapper,Configuration#getMapper调用的是MapperRegistry#getMapper。
public T getMapper(Class type, SqlSession sqlSession) {
// 获取Mapper接口对应的MapperProxyFactory
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException(“Type " + type + " is not known to the MapperRegistry.”);
}
try {
// 创建Mapper接口实例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
三、MapperProxyFactory
3.1 根据MapperProxy创建Mapper接口代理实例
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
3.2 根据SqlSession创建Mapper接口代理实例
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
四、MapperProxy
4.1 反射调用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 (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
4.2 缓存Mapper接口方法对应的MapperMethod
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
4.3 invokeDefaultMethod
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
4.4 isDefaultMethod
private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
&& method.getDeclaringClass().isInterface();
}
五、MapperMethod
5.1 构造函数
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
5.2 SqlCommand
5.2.1 SqlCommand构造函数
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
** configuration);
if (ms == null) {
// 获取方法上的@Flush注解
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + “.” + methodName);
}
} else {
name = ms.getId();
// select|insert|update|delete等
** type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
5.2.2 根据方法完全限定名获取MappedStatement resolveMappedStatement
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
// Mapper类完全限定名 + 方法名
String statementId = mapperInterface.getName() + “.” + methodName;
// 判断是否存在对应的MappedStatement即select|insert|update|delete语句
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
// 查询父接口上方法对应的MappedStatement
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
** declaringClass, configuration)**;
if (ms != null) {
return ms;
}
}
}
return null;
}
5.3 MethodSignature
5.3.1 解析方法参数为SQL使用的参数
public Object convertArgsToSqlCommandParam(Object[] args) {
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:6.2
return paramNameResolver.getNamedParams(args);
}
5.4 执行Mapper接口方法
调用sqlSession相应方法执行数据库操作。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
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 {
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
Object param = method.convertArgsToSqlCommandParam(args);
// 查询单条结果
result = sqlSession.selectOne(command.getName(), param);
}
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;
}
5.5 更新操作(insert|update|delete)结果转换
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) {
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
result = (long)rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
result = rowCount > 0;
} else {
throw new BindingException(“Mapper method '” + command.getName() + "’ has an unsupported return type: " + method.getReturnType());
}
return result;
}
5.6 ResultHandler处理执行结果
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
if (!StatementType.CALLABLE.equals(ms.getStatementType())
&& void.class.equals(ms.getResultMaps().get(0).getType())) {
throw new BindingException(“method " + command.getName()
+ " needs either a @ResultMap annotation, a @ResultType annotation,”
+ " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
}
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
// 分页查询
RowBounds rowBounds = method.extractRowBounds(args);
sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
} else {
sqlSession.select(command.getName(), param, method.extractResultHandler(args));
}
}
5.7 查询结果多条处理
private Object executeForMany(SqlSession sqlSession, Object[] args) {
List result;
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
// 分页查询
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(command.getName(), param);
}
// 实际返回类型不是List需要转换
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
// 转换成数组
private Object convertToArray(List list) {
Class<?> arrayComponentType = method.getReturnType().getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
if (arrayComponentType.isPrimitive()) {
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return array;
} else {
return list.toArray((E[])array);
}
}
// 转换成实际返回集合类型
private Object convertToDeclaredCollection(Configuration config, List list) {
// 这里用到了ObjectFactory
Object collection = config.getObjectFactory().create(method.getReturnType());
MetaObject metaObject = config.newMetaObject(collection);
metaObject.addAll(list);
return collection;
}
5.8 查询结果类型Map
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
Map<K, V> result;
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
// 分页查询
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else {
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
}
return result;
}
5.9 查询结果类型Cursor
private Cursor executeForCursor(SqlSession sqlSession, Object[] args) {
Cursor result;
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectCursor(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectCursor(command.getName(), param);
}
return result;
}
六、ParamNameResolver
6.1 构造函数
public ParamNameResolver(Configuration config, Method method) {
final Class<?>[] paramTypes = method.getParameterTypes();
// 获取参数注解
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// 参数实现了RowBounds、ResultHandler接口忽略不处理
if (isSpecialParameter(paramTypes[paramIndex])) {
continue;
}
String name = null;
// 获取参数上的@Param注解,得到可以在SQL中使用的参数名
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// 获取真实参数名
if (config.isUseActualParamName()) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// 使用参数位置索引
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
// 参数未知索引与参数名映射关系
names = Collections.unmodifiableSortedMap(map);
}
private static boolean isSpecialParameter(Class<?> clazz) {
return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
}
6.2 获取参数名 getNamedParams
既可以通过@Param指定的参数名获取参数,也可以通过param1, param2, …获取参数。
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// 参数名与参数值映射关系
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, …)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// 确保不覆盖@Param注解命名的参数
if (!names.containsValue(genericParamName)) {
// param1, param2, …与参数值映射关系
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}