Mapper代理模式产生的原因:
由于使用MyBatis框架时Mapper接口实现类中存在代码重复,整个操作过程模板重复(加载配置文件/创建SqlSessionFactory/生产SqlSession),故MyBatis推出了基于JDK动态代理自动生成代理Mapper接口实现类,代理类可以完成Mapper中定义的增删改查操作,避免代码重复工作。
MapperRegistry加载原理
在调用SqlSession.getMapper()方法时可以发现底层调用的是mapperRegistry.getMapper(type, sqlSession);那MapperRegistry又是什么呢?MapperRegistry封装了全局配置文件Configuration和
Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
knownMappers 的key是全部加载的Mapper接口,value是每个Mapper接口对于的代理类生成工厂。下面看看MapperRegistry是如何加载的:
1.进入XMLConfigBuilder的mapperXML解析方法mapperElement(XNode parent)方法,在mapper标签为package或者class格式是配置的为Mapper接口的包路径或Mapper接口的全限定名
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
// 扫描Mapper接口方法
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 扫描Mapper接口方法 this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
2.进入configuration.addMapper(type)发现调用的是mapperRegistry.addMapper(type)
public <T> void addMapper(Class<T> type) {
//判断是否是接口
if (type.isInterface()) {
// 判断是否已经扫描过了
if (this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 为当前Mapper接口创建代理类生成工厂
this.knownMappers.put(type, new MapperProxyFactory(type));
// 扫描当前Mapper接口中的注解,并解析后封装到核心配置类Configuration中
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
// 如果解析注解过程中报错则剔除当前Mapper接口及其代理类生成工厂
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
3.进入new MapperProxyFactory(type)代理类生成工厂的构造
// mapper接口类型
private final Class<T> mapperInterface;
// 已执行方法缓存
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
总结:MapperRegistry在解析Mapper标签是初始化并封装
SqlSession.getMapper()方法原理
1.底层调用的mapperRegistry.getMapper(type, sqlSession);
private final Configuration config;
//
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
// 取到mapper对应的代理类生成工厂后,调用newInstance方法获取mapper代理类的实例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
2.进入newInstance(sqlSession);方法
MapperProxyFactory.class
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
protected T newInstance(MapperProxy<T> mapperProxy) {
//根据JDK动态代理生成mapper的代理类返回
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// MapperProxy实现了InvocationHandler接口的invoke
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
// 调用重载方法
return this.newInstance(mapperProxy);
}
代理类方法执行原理(invoke方法剖析)
1.进入MapperProxy类
MapperProxy.class
// 当前调用getMapper方法的SqlSession
private final SqlSession sqlSession;
// 被执行方法所属Mapper接口的类型
private final Class<T> mapperInterface;
// 当前mapper已执行方法缓存
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 被执行方法是否Object的方法如toString/equals等,如果是直接执行原方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//是否构造方法
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
// 获取当前方法相关参数
MapperMethod mapperMethod = this.cachedMapperMethod(method);
// 执行execute方法
return mapperMethod.execute(this.sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
2.进入mapperMethod.execute(this.sqlSession, args);方法,根据方法的相关参数及类型判断执行sqlSession的增删改查方法。
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}```