上一篇文章我们了解了什么是mybatis,并通过一个用例实现了一个简单的mybatis查询数据库的数据,在文章的最后针对mybatis如何实现这些功能提出了几个问题。接下来的文章中会对这些问题做一一解答。
本篇文章中解决第一个问题,mybatis为什么调用接口方法就可以实现语句的查询?
实现该功能主要分成两块进行:
注册部分关键代码:
XmlConfigBuilder.java
private void parseConfiguration(XNode root) {
try {
......
//(本文关注点)解析mappers标签,将mapper注册到MapperRegistry中
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
/**
*解析mappers标签下的mapper标签,可通过标签属性获取资源package、class、url、resource四种方式获取mapper资源
**/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
//遍历获取mappers标签下的所有子标签(package/mapper)
for (XNode child : parent.getChildren()) {
//如果mappers下子标签package,则解析name属性该包下的所有类,并注册到MapperRegistry中
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//解析mapper标签, resource、class、url属性有且只能配置一个,解析属性中的内容,注册到MapperRegistry中
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
Configuration.java
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
public void addMapper(Class type) {
mapperRegistry.addMapper(type);
}
public void addMappers(String packageName, Class> superType) {
mapperRegistry.addMappers(packageName, superType);
}
MapperRegistry.java
/**
*该类主要用于记录注册的Mapper的动态代理对象。
**/
public class MapperRegistry {
private Configuration config;
//注册器
private final Map, MapperProxyFactory>> knownMappers = new HashMap, MapperProxyFactory>>();
public MapperRegistry(Configuration config) {
this.config = config;
}
/**
*获取mapper核心代码,即测试用例中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);
}
}
public boolean hasMapper(Class type) {
return knownMappers.containsKey(type);
}
/**
*注册mapper核心代码
**/
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 {
//注册mapper接口对象, key为接口对象, value为mapper代理工厂,下面我们看下MpperProxyFactory中有哪些东东
knownMappers.put(type, new MapperProxyFactory(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
//解析接口方法上的注解@Select(Provider) @Update(Provider) @Delete(Provider) @Insert(Provider)
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
/**
* @since 3.2.2
*/
public Collection> getMappers() {
return Collections.unmodifiableCollection(knownMappers.keySet());
}
/**
* @since 3.2.2
*/
public void addMappers(String packageName, Class> superType) {
ResolverUtil> resolverUtil = new ResolverUtil>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set>> mapperSet = resolverUtil.getClasses();
for (Class> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
/**
* @since 3.2.2
*/
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
}
/**
* @author Lasse Voss
*/
public class MapperProxyFactory {
private final Class mapperInterface;
private Map methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class getMapperInterface() {
return mapperInterface;
}
public Map getMethodCache() {
return methodCache;
}
//哈哈,终于找到了,这里创建了一个Mapper的代理对象,并持有MapperProxy对象的引用
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
下面是动态代理生成的类的部分信息:
public final class $Proxy0 extends Proxy implements Proxy9 {
......
private static Method m3;
......
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
......
public final List queryTestDO(int var1) throws {
try {
//通过反射调用MapperProxy类的invoke方法。在invoke方法中实现具体的数据库操作逻辑
return (List)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
.......
static {
try {
......
m3 = Class.forName("com.sun.proxy.$Proxy9").getMethod("queryTestDO", Integer.TYPE);
......
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
综上所述,调用接口方法其实最终调用的是MapperProxy的invoke方法,现在我们预览下MapperProxy中invoke方法做了哪些东西,接下来我们会分析方法中具体的实现。
public class MapperProxy implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class mapperInterface;
private final Map methodCache;
public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//为什么需要加这个判断呢?如果调用的是Object的toString(),equals(),hashCode()等方法的时候,直接反射调用Object对象的方法。否则调用sql执行方法
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行sql
return mapperMethod.execute(sqlSession, args);
}
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;
}
}