栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring整合MyBatis原理之Dao接口的解析及其调用 (三)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring整合MyBatis原理之Dao接口的解析及其调用 (三)

目录

前言解析 `Dao` 接口小结`Dao` 接口的调用

`method.invoke(this, args)`增删改查 `spring` 整合 `mybatis` 原理流程步骤

前言

本篇文章是 Spring 整合 MyBatis 原理的第三篇文章,上一篇文章 在这里 ,我们啦继续学习 Spring 整合 MyBatis 原理

解析 Dao 接口

DAO 文件,也就是 basePackage 指定的包下的文件,也就是上文的 ProductInfoMapper

上文 doScan() 中说过,basePackage 包下所有 bean 定义的 beanClass 会被设置成 MapperFactoryBean.class,而 MapperFactoryBean 也实现了 FactoryBean 接口,因此直接看 MapperFactoryBean 的 getObject 方法

@Override
public T getObject() throws Exception {
  	// 1.从父类中拿到sqlSessionTemplate,这边的sqlSessionTemplate也是doScan中添加的属性
  	// 2.通过mapperInterface获取mapper
  	return getSqlSession().getMapper(this.mapperInterface);
}
 
// SqlSessionTemplate
@Override
public  T getMapper(Class type) {
  	return getConfiguration().getMapper(type, this);
}
 
// Configuration.java
public  T getMapper(Class type, SqlSession sqlSession) {
  	return mapperRegistry.getMapper(type, sqlSession);
}
 
// MapperRegistry.java
public  T getMapper(Class type, SqlSession sqlSession) {
  	// 1.从knownMappers缓存中获取
  	final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
  	if (mapperProxyFactory == null) {
    	throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  	}
  	try {
    	// 2.新建实例
    	return mapperProxyFactory.newInstance(sqlSession);
  	} catch (Exception e) {
    	throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  	}
}
 
// MapperProxyFactory.java
public T newInstance(SqlSession sqlSession) {
  	// 1.构造一个MapperProxy
  	final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  	// 2.使用MapperProxy来构建实例对象
  	return newInstance(mapperProxy);
}
 
protected T newInstance(MapperProxy mapperProxy) {
  	// 使用 JDK 动态代理来代理要创建的实例对象,InvocationHandler为 mapperProxy,
  	// 因此当我们真正调用时,会走到 mapperProxy 的 invoke 方法
  	return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
小结
    通过 mapperInterface 从 knownMappers 缓存中获取到 MapperProxyFactory 对象通过 JDK 动态代理创建 mappper 接口代理对象,InvocationHandler 的实现类为 MapperProxy
Dao 接口的调用

当 Dao 中的接口被调用时,因为 MapperProxy 类实现了 InvocationHandler 接口,所以会调用到 MapperProxy 的 invoke() 方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  	try {
    	if (Object.class.equals(method.getDeclaringClass())) {
      		return method.invoke(this, args);
    	} else {
      		// 1.创建MapperMethodInvoker
      		// 2.将method -> MapperMethodInvoker放到methodCache缓存
      		// 3.调用MapperMethodInvoker的invoke方法
      		return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    	}
  	} catch (Throwable t) {
    	throw ExceptionUtil.unwrapThrowable(t);
  	}
}
 
// MapperProxy.java
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
  	try {
    	// 1.放到methodCache缓存,key为method,value为MapperMethodInvoker
    	return methodCache.computeIfAbsent(method, m -> {	
      		if (m.isDefault()) {
        		// 2.方法为默认方法,Java8之后,接口允许有默认方法
        		try {
          			if (privateLookupInMethod == null) {
            			return new DefaultMethodInvoker(getMethodHandleJava8(method));
          			} else {
	            		return new DefaultMethodInvoker(getMethodHandleJava9(method));
          			}
        		} catch (IllegalAccessException | InstantiationException | InvocationTargetException
            | NoSuchMethodException e) {
          			throw new RuntimeException(e);
        		}
      		} else {
        		// 3.正常接口会走这边,使用mapperInterface、method、configuration
        		// 构建一个MapperMethod,封装成PlainMethodInvoker
        		return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      		}
    	});
  	} catch (RuntimeException re) {
    	Throwable cause = re.getCause();
    	throw cause == null ? re : cause;
	}
}
method.invoke(this, args)
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
  	return mapperMethod.execute(sqlSession, args);
}
 
// MapperMethod.java
public Object execute(SqlSession sqlSession, Object[] args) {
  	Object result;
  	// 1.根据命令类型执行来进行相应操作
  	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()) {
        		executeWithResultHandler(sqlSession, args);
        		result = null;
      		} else if (method.returnsMany()) {
        		result = executeForMany(sqlSession, args);
      		} else if (method.returnsMap()) {
        		result = executeForMap(sqlSession, args);
      		} else if (method.returnsCursor()) {
        		result = executeForCursor(sqlSession, args);
      		} else {
        		Object param = method.convertArgsToSqlCommandParam(args);
        		result = sqlSession.selectOne(command.getName(), param);
        		if (method.returnsOptional()
            		&& (result == null || !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());
  	}
  	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;
}

根据不同的操作类型执行相应的操作,最终将结果返回

增删改查
// 1.insert
@Override
public int insert(String statement, Object parameter) {
  	return update(statement, parameter);
}
 
// 2.update
@Override
public int update(String statement, Object parameter) {
  	try {
    	dirty = true;
    	// 从mappedStatements缓存拿到对应的MappedStatement对象,执行更新操作
    	MappedStatement ms = configuration.getMappedStatement(statement);
    	return executor.update(ms, wrapCollection(parameter));
  	} catch (Exception e) {
    	throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
  	} finally {
    	ErrorContext.instance().reset();
  	}
}
 
// 3.delete
@Override
public int delete(String statement, Object parameter) {
  	return update(statement, parameter);
}
 
// 4.select,以executeForMany为例
private  Object executeForMany(SqlSession sqlSession, Object[] args) {
  	
  	List result;
  	// 1.参数转换成sql命令参数
  	Object param = method.convertArgsToSqlCommandParam(args);
  	if (method.hasRowBounds()) {
    	RowBounds rowBounds = method.extractRowBounds(args);
    	result = sqlSession.selectList(command.getName(), param, rowBounds);
  	} else {
    	// 2.执行查询操作
    	result = sqlSession.selectList(command.getName(), param);
  	}
  	
  	// 3.处理返回结果
  	if (!method.getReturnType().isAssignableFrom(result.getClass())) {
    	if (method.getReturnType().isArray()) {
      		return convertToArray(result);
    	} else {
      		return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
    	}
  	}
  	return result;
}
 
@Override
public  List selectList(String statement, Object parameter) {
  	return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
 
@Override
public  List selectList(String statement, Object parameter, RowBounds rowBounds) {

  	try {
    	// 从mappedStatements缓存中拿到对应的MappedStatement对象,执行查询操作
    	MappedStatement ms = configuration.getMappedStatement(statement);
    	return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  	} catch (Exception e) {
    	throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  	} finally {
    	ErrorContext.instance().reset();
  	}
}

可以看出,最终都是从 mappedStatements 缓存中拿到对应的 MappedStatement 对象,执行相应的操作

spring 整合 mybatis 原理流程步骤
    扫描注册 basePackage 包下的所有的 mapper 接口类,将 mapper 接口类封装成为 BeanDefinition 对象,注册到 spring 容器中,同时会将 basePackage 包下的所有 bean 进行一些特殊处理:beanClass 设置为 MapperFactoryBean、bean 的真正接口类作为构造函数参数传入 MapperFactoryBean解析 mapperLocations 配置的 mapper 文件,将 mapper 文件中的每个 SQL 封装成 MappedStatement,放到 mappedStatements 缓存中,key 为 id,例如: id 为 :com.atguigu.mapper.ProductInfoMapper.selectByPrimaryKey,value 为 MappedStatement。并且将解析过的 mapper 文件的 namespace 放到 knownMappers 缓存中,key 为 namespace 对应的 class,value 为 MapperProxyFactory创建 DAO 的 bean 时,通过 mapperInterface 从 knownMappers 缓存中获取到 MapperProxyFactory 对象,通过 JDK 动态代理创建 mapper 接口代理对象,实现了 InvocationHandler 接口的类为 MapperProxyDAO 中的接口被调用时,通过 mapper 接口代理对象,调用 MapperProxy 的 invoke 方法,执行相应的增删改查操作
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/755862.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号