Mybatis源码学习-核心流程-代理封装阶段
所用到的设计模块:
策略模式、门面模式
策略模式: 定义了一系列的算法,并将每个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化
使用场景:
1.同一类型问题的多种处理方式,仅仅是具体行为有差别。
2.出现同意抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时
例如: @Resource(name=xxx) 就是一个明显的策略模式
门面模式:指提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口
mybatis的代理封装阶段
我们使用Mybatis时,表面上是调用Mapper接口的方法,但实际上,Mybatis的内部,将Mapper接口调用的请求转发给了SqlSession
那什么是sqlSession呢?
SqlSession是MyBatis对外提供的最关键的核心接口,通过SqlSession可以让客户端与数据库进行连接,并执行数据库读写命令、获取映射器、管理事务等操作。
所有的数据库访问请求的都是由SqlSession来处理的。
SqlSession默认实现类为DefaultSqlSession
1.SqlSession是MyBatis的门面,是MyBatis对外提供数据访问的主要API
2.实际上,SqlSession的功能都是基于Executor来实现的(单一职责原则)
例如查询请求: 都会转发到Executor.query方法来执行
SqlSession由SqlSessionFactory创建,SqlSessionFactory全局唯一、单例。
SqlSessionFactory:
SqlSessionFactory使用了工厂模式,来创建SqlSession
默认实现类为DefaultSqlSessionFactory ,
核心方法为openSessionFromDataSource(ExecutorType, TransactionIsolationLevel, boolean)
这个方法就是从数据源获取数据库连接
//从数据源获取数据库连接
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//获取mybatis配置文件中的environment对象
final Environment environment = configuration.getEnvironment();
//从environment获取transactionFactory对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建事务对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据配置创建executor
final Executor executor = configuration.newExecutor(tx, execType);
//创建DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
调用Mapper接口的方法时,如何将请求转发给Sqlsession?
有三个关键要素
1.SqlSession中对应的方法执行
2.命名空间跟方法名
3.传递参数
由binding模块来实现上述步骤
binding模块核心类
MapperRegistry:Mapper接口和对应的代理对象工厂的注册中心。
主要是通过addMapper方法,将接口的工厂类加入到注册中心中,便于生成代理的实例对象
//记录了mapper接口与对应MapperProxyFactory之间的关系
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
//将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接口的代理工程类,并将信息添加至knownMappers------
knownMappers.put(type, new MapperProxyFactory<T>(type));
//解析接口上的注解信息,并添加至configuration对象
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
MapperProxyFactory:用于生成mapper接口动态代理的实例对象;保证Mapper实例对象是局部变量
public class MapperProxyFactory<T> {
//mapper接口的class对象
private final Class<T> mapperInterface;
//key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
// 此处省略了......
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// ------创建实现了mapper接口的动态代理对象--------
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//每次调用都会创建新的MapperProxy对象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
MapperProxy:实现了InvocationHandler接口,增强mapper接口的实现
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;//记录关联的sqlsession对象
private final Class<T> mapperInterface;//mapper接口对应的class对象;
//key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享
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;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增强
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// ------从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中------
final MapperMethod mapperMethod = cachedMapperMethod(method);
//调用execute方法执行sql
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
}
MapperMethod:封装了Mapper接口中对应方法的信息,以及对应的sql语句的信息,是Mapper接口与映射配 置文件中sql语句的桥梁。
因为不记录任何状态信息,所以它可以在多个代理对象之间共享。
它有几个关键的数据结构:
内部类 - - - SqlCommand:从configuration中获取方法的命名空间、方法名以及SQL语句的类型
内部类 - - - MethodSignature:封装mapper接口方法的相关信息 (入参、返回类型)
ParamNameResolver:解析mapper接口方法中的入参,将多个参数转成Map
运行流程:
从SqlSession.getMapper(Class ) 开始,时序图如下
该流程的核心是在三步翻译那里 ,主要方法MapperMethod的为execute方法
一、通过sql语句的类型、返回参数类型,来确定调用SqlSession中的某个方法
二、通过SqlCommand.name 生成二维坐标
三、通过MethodSignature.paramNameResolve 将传入的多个参数转成Map进行参数传递
具体代码如下:
//三步翻译在此完成
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//第一步 根据sql语句类型以及接口返回的参数选择调用不同的方法
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()) {//返回值为void
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {//返回值为集合或者数组
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {//返回值为map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {//返回值为游标
result = executeForCursor(sqlSession, args);
} else {//处理返回为单一对象的情况
//通过参数解析器解析解析参数
Object param = method.convertArgsToSqlCommandParam(args);//第三步翻译,讲入参转化成Map
result = sqlSession.selectOne(command.getName(), param); // 第二步,从SqlCommand中获取二维坐标
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = OptionalUtil.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;
}