Mybatis源码剖析:Mapper代理方式

Mybatis源码剖析:Mapper代理方式

回顾下写法:

public static void main(String[] args) {
    	...
    //前三步都相同
    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = factory.openSession();
    //这里不再调用SqlSession的api,而是获得了接口对象,调用接口中的方法。
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> list = mapper.getUserByName("tom");
}

思考一个问题,通常的Mapper接口我们都没有实现的方法却可以使用,是为什么呢?答案很简单—动态代理

MyBatis初始化时对接口的处理:MapperRegistry是Configuration中的一个属性, 它内部维护一个HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类。mappers中可以 配置接口的包路径,或者某个具体的接口类。

<mappers>
    <mapper class="com.tao.mapper.UserMapper"/>
    <package name="com.tao.mapper"/>
</mappers>

当解析mappers标签时,它会判断解析到的是mapper配置文件时,会再将对应配置文件中的增删 改查 标签 封装成MappedStatement对象,存入mappedStatements中。当判断解析到接口时,会 建此接口对应的MapperProxyFactory对象,存入HashMap中,key =接口的字节码对象,value =此接口对应的MapperProxyFactory对象。

1、源码剖析-getmapper()

进入 sqlSession.getMapper(UserMapper.class )中

//DefaultSqlSession 中的 getMapper
public <T> T getMapper(Class<T> type) {
	return configuration.<T>getMapper(type, this);
}

//configuration 中的 getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	return mapperRegistry.getMapper(type, sqlSession);
}

//MapperRegistry 中的 getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	//从 MapperRegistry 中的 HashMap 中拿 MapperProxyFactory
	final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>)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);
	}
}

//MapperProxyFactory 类中的 newInstance 方法
public T newInstance(SqlSession sqlSession) {
	//创建了 JDK动态代理的Handler类
	final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
	//调用了重载方法
	return newInstance(mapperProxy);
}
                           
//MapperProxy 类,实现了 InvocationHandler 接口
public class MapperProxy<T> implements InvocationHandler, Serializable {
	//省略部分源码    
    
    private final SqlSession sqlSession;
	private final Class<T> mapperInterface;
	private final Map<Method, MapperMethod> methodCache;
    
	//构造,传入了 SqlSession,说明每个session中的代理对象的不同的!
	public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
	this.sqlSession = sqlSession;
	this.mapperInterface = mapperInterface;
	this.methodCache = methodCache;
	}
	//省略部分源码
}

2、 源码剖析-invoke()

在动态代理返回了示例后,我们就可以直接调用mapper类中的方法了,但代理对象调用方法,执行是在MapperProxy中的invoke方法中。

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);
    }
    // 获得 MapperMethod 对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //重点在这:MapperMethod最终调用了执行的方法
    return mapperMethod.execute(sqlSession, args);
}

进入execute方法:

public Object execute(SqlSession sqlSession, Object[] args) {
	Object result;
	//判断mapper中的方法类型,最终调用的还是SqlSession中的方法 
    switch(command.getType()) {
		case INSERT: {
			//转换参数
			Object param = method.convertArgsToSqlCommandParam(args);
			//执行INSERT操作
			// 转换 rowCount
			result = rowCountResult(sqlSession.insert(command.getName(), param));
			break;
		}
		case UPDATE: {
			//转换参数
			Object param = method.convertArgsToSqlCommandParam(args);
			// 转换 rowCount
			result = rowCountResult(sqlSession.update(command.getName(), param));
			break;
		}
		case DELETE: {
			//转换参数
			Object param = method.convertArgsToSqlCommandParam(args);
			// 转换 rowCount
			result = rowCountResult(sqlSession.delete(command.getName(), param));
			break;
		}
		case SELECT:
			//无返回,并且有ResultHandler方法参数,则将查询的结果,提交给 ResultHandler 进行处理
			if (method.returnsVoid() && method.hasResultHandler()) {
				executeWithResultHandler(sqlSession, args);
				result = null;
			//执行查询,返回列表
			} else if (method.returnsMany()) {
				result = executeForMany(sqlSession, args);
			//执行查询,返回Map
			} else if (method.returnsMap()) {
				result = executeForMap(sqlSession, args);
			//执行查询,返回Cursor
			} else if (method.returnsCursor()) {
				result = executeForCursor(sqlSession, args);
			//执行查询,返回单个对象
			} else {
				//转换参数
				Object param = method.convertArgsToSqlCommandParam(args);
				//查询单条
				result = sqlSession.selectOne(command.getName(), param);
				if (method.returnsOptional() &&c(result == null ||c!method.getReturnType().equals(result.getClass()))){
					result = Optional.ofNullable(result);
				}
			}
			break;
		case FLUSH:
			result = sqlSession.flushStatements();
			break;
		default:
			throw new BindingException("Unknown execution method for: " + command.getName());
	}
	//返回结果为null,并且返回类型为基本类型,则抛出BindingException异常
	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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值