一、回顾 Mapper 代理写法
思考⼀个问题,通常的Mapper接口我们都没有实现的方法却可以使用,是为什么呢?
答案很简单:动态代理
开始之前介绍⼀下 MyBatis 初始化时对接口的处理:MapperRegistry 是 Configuration 中的⼀个属性,它内部维护⼀个 HashMap 用于存放 mapper 接口的工厂类,每个接口对应⼀个工厂类
/**
* mapper代理方式
*/
@Test
public void test2() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = factory.openSession();
// 使用JDK动态代理对mapper接口产生代理对象
IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
//代理对象调用接口中的任意方法,执行的都是动态代理中的invoke方法
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
}
mappers 标签中可以配置接口的包路径,或者某个具体的接口类
<!--引入映射配置文件-->
<mappers>
<package name="com.wujun.mapper"/>
</mappers>
二、初始化 Mapper
- 之前在说 Mybatis源码剖析 -- 初始化过程(传统方式)的时候,提到过
XMLConfigBuilder.parseConfiguration(XNode root)
这个方法里面会对xml进行解析,那么 mappers 的解析也在里面// 解析 <mappers /> 标签 mapperElement(root.evalNode("mappers"));
private void mapperElement(XNode parent) throws Exception { if (parent != null) { // 遍历子节点 for (XNode child : parent.getChildren()) { // 如果是 package 标签,则扫描该包 if ("package".equals(child.getName())) { // 获取 <package> 节点中的 name 属性 String mapperPackage = child.getStringAttribute("name"); // 从指定包中查找 mapper 接口,并根据 mapper 接口解析映射配置 configuration.addMappers(mapperPackage); // 如果是 mapper 标签, } else { // 获得 resource、url、class 属性 String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); // resource 不为空,且其他两者为空,则从指定路径中加载配置 if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); // 获得 resource 的 InputStream 对象 InputStream inputStream = Resources.getResourceAsStream(resource); // 创建 XMLMapperBuilder 对象 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); // 执行解析 mapperParser.parse(); // url 不为空,且其他两者为空,则通过 url 加载配置 } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); // 获得 url 的 InputStream 对象 InputStream inputStream = Resources.getUrlAsStream(url); // 创建 XMLMapperBuilder 对象 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); // 执行解析 mapperParser.parse(); // mapperClass 不为空,且其他两者为空,则通过 mapperClass 解析映射配置 } else if (resource == null && url == null && mapperClass != null) { // 获得 Mapper 接口 Class<?> mapperInterface = Resources.classForName(mapperClass); // 添加到 configuration 中 configuration.addMapper(mapperInterface); // 以上条件不满足,则抛出异常 } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
- ,调用
configuration.addMapper(mapperPackage)
方法将包名或者接口传入 mapper 注册表,存入一个名叫 knownMappers 的 HashMap 中,key就是接口的类型,value就是针对这个接口所生成的代理对象/** * Mapper 注册表 * * @author Clinton Begin * @author Eduardo Macarron * @author Lasse Voss */ public class MapperRegistry { /** * MyBatis Configuration 对象 */ private final Configuration config; /** * MapperProxyFactory 的映射 * * KEY:Mapper 接口 */ //这个类中维护一个HashMap存放MapperProxyFactory private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
- 接着看
getMapper()
的源码public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 获得 MapperProxyFactory 对象 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); // 不存在,则抛出 BindingException 异常 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); } }
public T newInstance(SqlSession sqlSession) { // 创建了JDK动态代理的invocationHandler接口的实现类mapperProxy final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); // 调用了重载方法 return newInstance(mapperProxy); }
protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy); }
-
invoke()
方法源码,其实本质上还是调用了 sqlSession 里面的增删改查方法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); }
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() && (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()); } // 返回结果为 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; }