在进行动态代理前,我们需要在mybatis的全局配置环境中配置mapper标签,告诉mybatis我的mapper都在哪,同时需要在项目中创建mapper的xml文件和对应的接口
注意:mapper的xml文件要与接口名一样,namespace也需要设置为接口的全路径名,只有这样才能在为mapper接口做动态代理的时候找到正确的sql语句
1. DefaultSqlSession:sqlsession的实现类,对jdbc的connection进行了封装
2. Configuation:mabatis配置文件对应的类,记录了mybatis的配置信息
3. MapperRegistry:Mapper接口的注册,通过内部的addMapper方法将指定位置的mapper接口存储到
knownMappers(该类的一个字段,Map *< Class >, MapperProxyFactory > >)中 **
4. MapperProxyFactory:该类是代理工厂类,内置了有mapper接口的class和Map
5. MapperProxy:代理对象实现类,内置了一个methodCache的map存放键值对(method,MapperMethod),还有mapper接口的class和sqlsession对象
6. MapperMethod:mapper接口方法对应的实现方法,当我们执行mapper中的方法时,都是通过mapperProxy中的methodCache找到method对应的MapperMethod,最后MapperMethod的execute方法(execute内部是执行sqlsession的方法)
7. 注意:
public class MapperMethod {
//command根据configuration处理mapper接口方法对应xml文件中的sql语句
//method根据configuation处理方法的参数以及根据返回类型将sqlsession返回的结果进行封装
//这也是要求mapper接口与xml文件同名并且namespace为接口全路径的原因所在
private final MapperMethod.SqlCommand command;
private final MapperMethod.MethodSignature method;
}
业务代码
FlowerMapper mapper = sqlSession.getMapper(FlowerMapper.class);
DefaultSqlSession
public T getMapper(Class type) {
//此时传入了this,也就是我们业务代码中的sqlSession
return this.configuration.getMapper(type, this);
}
Configuration
public T getMapper(Class type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
上面的步骤比较简单,就是通过模块之间的调用将mapper的class和sqlsession传入了mapperRegistry的getMapper中
mapperRegistry
public T getMapper(Class type, SqlSession sqlSession) {
//knownMapper包含了扫描到的mapper的接口
//判断传进来的class是否在knownMapper中,是的话则将其转化为代理工厂,不是则抛出异常
MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
MapperProxyFactory
public class MapperProxyFactory {
//传进来的mapper的class
private final Class mapperInterface;
private final Map methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class getMapperInterface() {
return this.mapperInterface;
}
public Map getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//Mapper的代理对象实现类
MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
//返回动态代理对象
return this.newInstance(mapperProxy);
}
}
MapperProxy
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;
}
//任何mapper对象的方法都是通过invoke来实现调用的
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (method.isDefault()) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
//通过原mapper接口的方法(接口的方法没有方法体)得到对应的实现方法(有方法体)
MapperMethod mapperMethod = this.cachedMapperMethod(method);
//重点分析这一句
//执行该方法
return mapperMethod.execute(this.sqlSession, args);
}
//将method和mapperMethod作为键值对传入methodCache中
private MapperMethod cachedMapperMethod(Method method) {
//若该method对应的mapperMethod为空则构造一个mapperMethod
return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {
return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
});
}
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
Constructor constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
Class> declaringClass = method.getDeclaringClass();
return ((Lookup)constructor.newInstance(declaringClass, 15)).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
}
MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch(this.command.getType()) {
case INSERT:
//将参数封装成一个object,传进sqlSession的方法中
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);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
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;
}
}
通过上面的源码简单分析了mapper的动态代理过程,大致如下:
而execute方法内部还是通过调用sqlSession的api来执行mapper操作的,也就是mapper接口中方法的实现最终还是依靠sqlsession,mapper的存在是为了让程序员的开发效率更高