大家都晓得我的格调,喜爱用故事带入技术学习。
然而 … 我讲源码怎么用故事带入呢?
用我的万能故事模板,小明探宝旅程。
这天小明来到的 Mybatis 王国,他问门口老者,这城门上写的是 iBatis, 怎么改成 Mybatis 了呢。
老者答复:哦,本来呀这是 iBatis, 这现在啊改名 Mybatis 了。
小明挠了挠头,老者答复了问题,但又如同没答复。
反正晓得了,当初就是 Mybatis。
小明晓得 Mybatis 王国有一神奇之物,名为 Mapper 代理。
因为对此物的执着,小明带着他的武器(IntelliJ IDEA)进入城内一探到底。
一、好奇心
好奇心是驱动咱们人类倒退必不可少的因素之一,都说好奇心害死猫,这句话在技术畛域是不成立的,身为技术人员肯定要对事物充斥好奇(我说的是技术畛域的事物,你可别对特叔叔的服务好奇,就去找特叔叔)
你平时用 Mybatis 没有什么能让你好奇的吗?
你创立一个 Mapper 接口,而后写一个 Mapper.xml
最初间接应用 Mapper 接口就能进行增删改查操作了。
首先它是个接口呀,其次他怎么晓得执行什么操作呢?
不晓得你好奇不好奇,反正我很好奇,就是因为这份好奇心,我才来了这场探险之旅。
二、起伏的路
我曾经给大家趟平了路,有好奇心的同学能够跟着我的足迹来,或者你也能发现很多精彩。
首先得有小儿国,不对是有 Mybatis 王国。
pom.xml
增加 maven 依赖后记得 reload 我的项目,让他去下载对应 jar 包到本地仓库。
<dependencies>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--mysql 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="TestMapper.xml"/>
</mappers>
</configuration>
TestMapper.java
package dao;
import entity.TestEntity;
import java.util.List;
/**
* @author 木子的昼夜编程
*/
public interface TestMapper {List<TestEntity> list();
}
TestEntity.java
package entity;
import java.math.BigDecimal;
/**
* @author 木子的昼夜编程
*/
public class TestEntity {
private Long id;
private String name;
private BigDecimal salary;
// getter setter
}
TestMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.TestMapper">
<!-- 查问所有数据 -->
<select id="list" resultType="entity.TestEntity">
select * from test
</select>
</mapper>
Test.java
import dao.TestMapper;
import entity.TestEntity;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @author 木子的昼夜编程
*/
public class Test {public static void main(String[] args) throws IOException {
// 1. mybatis 配置文件
String resource = "mybatis-config.xml";
// 2. 获取输出流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 3. 创立 SqlSessionFactory 工厂 这一步会进行 Mapper 的动静代理操作
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 4. 创立 SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 5. 通过 sesson 获取 Mapper 这个 Mapper 会编程 Mybatis 的代理 Mapper
TestMapper mapper = session.getMapper(TestMapper.class);
// 6. 调用办法
List<TestEntity> list = mapper.list();
System.out.println(list);
}
}
}
通过我的摸索,次要逻辑在第三步就实现了,那句话怎么说来着,偷天换日。
前边咱们文章写到过:反射、动静代理、工厂模式
这里就是用了这 2 中技术,把咱们的 Mapper 进行了包装,你认为你用的是你本人的 Mapper,然而,你认为的你认为就是对的吗?是不对的。
1.
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
2.
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
3.
super(new Configuration());
有一个属性是:protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
画了一个简略的类图,其实就是在读取配置文件的时候创立了一个 MapperRegistry
而这个 MapperRegistry 就是存储宝藏(咱们写的接口 Mapper 的代理)的中央。
咱们能够看到什么时候进去的MapperRegistry
那咱们看一下什么时候用他的 addMapper 办法了,当然了还有 addMappers 办法
咱们就盯着 addMapper 办法剖析就阔以了,不用太执着
1. 上边曾经创立了 parser
parser.parse()
2. 能够看到这里开始解析配置文件了 configuration 就是咱们配置文件的根节点
parseConfiguration(parser.evalNode("/configuration"));
3. 看 Mapper 的话就看这个 解析 mappers 其余的标签能够先疏忽
mapperElement(root.evalNode("mappers"));
private void mapperElement(XNode parent) throws Exception {if (parent != null) {
// 开始循环遍历 mappers 的子标签 他的子标签能够使 mapper、package
for (XNode child : parent.getChildren()) {
// 如果是 package 巴拉巴拉一顿操作 咱们不看这个
if ("package".equals(child.getName())) {String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
// 咱们看这个 获取字标签的属性
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// 如果只配置了 resource 咱们只看这种形式 因为咱们配置的就是这种
// 读取资源
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 转成 XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 走解析
mapperParser.parse();} else if (resource == null && url != null && mapperClass == null) {
// 如果只配置了 url
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();} else if (resource == null && url == null && mapperClass != null) {
// 如果只配置了 class
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
// 一个 mapper 只能有一个属性 或者是 url 或者是 resource 或者是 class
// 如果你开发的时候报这个谬误了 那你应该就是配置了多个属性
// 通过我的验证 的确是 信我就好
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
XMLMapperBuilder.java
public void parse() {
// 判断是否解析过这个文件 解析过的都放在一个 set 中
if (!configuration.isResourceLoaded(resource)) {
// 进过了一系列操作
configurationElement(parser.evalNode("/mapper"));
// 放入 set 中标记为已解析过资源
configuration.addLoadedResource(resource);
// 开始绑定 Mapper 与 Mapper.xml
bindMapperForNamespace();}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();}
private void bindMapperForNamespace() {
// 获取命名空间 dao.TestMapper
String namespace = builderAssistant.getCurrentNamespace();
// 如果没有配置命名空间 是不会进行 Mapepr 与 Mapper.xml 的绑定的
// 如果 namespace 为空 前边解析会间接报异样 不晓得什么状况能走到这里
if (namespace != null) {
Class<?> boundType = null;
try {
// 获取命名空间对应的类 TestMapper.class
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// 如果找不见类 就算了 因为不是必须的
// 咱们本人的业务上也能够参考这种写法 其实就是
// if 类存在 巴拉巴拉一顿操作
// else 不操作
}
if (boundType != null) {
// 先判断是否曾经蕴含这个类了 其实是调用的 mapperRegistry 的 hasMapper
if (!configuration.hasMapper(boundType)) {
// 这里是为了适配 spring 做了 设置了一个标记 避免屡次加载这个资源
// 能够看 MapperAnnotationBuilder#loadXmlResource 理解更多
configuration.addLoadedResource("namespace:" + namespace);
// 咱们不关注那些 咱们只关注这个
configuration.addMapper(boundType);
}
}
}
}
还是咱们之前说的那句话,看代码尤其是源码,千万不要进黑洞,你肯定要明确你这次看的目标是什么。
就像我上边按个 configuration.addLoadedResource(“namespace:” + namespace); 这里你晓得是为了适配 Spring 做了一个标记就能够了,至于为什么适配,怎么做到的适配你不必管,你这次的目标应该很明确,就是想摸索 Mapper 代理是怎么代理的,所以千万千万不要陷进去。
Configuration.java
public <T> void addMapper(Class<T> type) {mapperRegistry.addMapper(type);
}
public class MapperRegistry {
// 这个是对 Configuration 的一个援用 因为注册的时候必定会用到一些个配置
private final Configuration config;
// 百宝箱 最初会寄存再这里 代理工厂 咱们最初代码调用 getMapper 就是用这个工厂给咱们创立一个
// 代理对象 咱们前几篇篇文章写得反射、代理模式、工厂模式 很贴合这里
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public MapperRegistry(Configuration config) {this.config = config;}
// 获取代理 代码个别调用的就是这个办法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 如没有 Mapper 类型对应的工厂 抛异样
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);
}
}
// 判断是否存在 hashMap 的 containsKey
public <T> boolean hasMapper(Class<T> type) {return knownMappers.containsKey(type);
}
// 注册 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 {
// 先占位 占位是十分重要的
// 如果不占位 就可能被尝试主动绑定
// 如果类型曾经存在就不会尝试 上边那个判断 hasMapper 就是在判断这个
// 没有很了解 不过无所谓 意思就是这样写比拟好
knownMappers.put(type, new MapperProxyFactory<T>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 解析进行一些初始化
parser.parse();
loadCompleted = true;
} finally {
// 如果没有加载胜利 从 map 中移除
if (!loadCompleted) {knownMappers.remove(type);
}
}
}
}
}
看过我昨天文章的人都晓得 接下来咱们看一下媒婆MapperProxyFactory
// 媒婆 负责介绍对象 负责创立咱们 Mapper 接口代理的工厂类
public class MapperProxyFactory<T> {
// 接口的 Class 对象
private final Class<T> mapperInterface;
// 办法对象 与 办法对象的封装
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
// 构造函数
public MapperProxyFactory(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}
public Class<T> getMapperInterface() {return mapperInterface;}
public Map<Method, MapperMethod> getMethodCache() {return methodCache;}
// 创立代理对象
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 创立一个代理类 并返回 至于这个 Proxy 能够看我前边动静代理的文章
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface},
mapperProxy);
}
// 这里是传入一个 sql 会话 而后创立一个 Mapper 接口代理类
public T newInstance(SqlSession sqlSession) {// 在这里创立了 Mapper 的代理 这个代理实现了 InvocationHandler(还是要看我前几篇动静代理文章)
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession,
mapperInterface,
methodCache);
return newInstance(mapperProxy);
}
}
MapperProxy.java
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.binding;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.apache.ibatis.lang.UsesJava7;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;
// JKD 动静代理 都须要实现 InvocationHandler
// 具体代理的事件 在 invoke 中做
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
// sqlSession
private final SqlSession sqlSession;
// 接口对象类型 TestMapper
private final Class<T> mapperInterface;
// 接口中的办法 list 等
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;
}
// 接口代理对象所有办法都会调用这里
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 判断是不是根底办法 toString hashCode 如果是的话间接调用不须要代理
if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
// 判断是不是 default 批改的办法 是的话非凡解决
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);
}
// 个别咱们会走到这里
// 缓存有的话 取缓存数据 没有的话 创立数据 放入缓存
// 敌人们 能够看到 Map 是个很神奇的存在 哪儿都有
// 所以面试钱筹备 肯定要筹备 map 相干常识
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
// 先判断有没有
MapperMethod mapperMethod = methodCache.get(method);
// 没有
if (mapperMethod == null) {
// 创立
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
// 放入缓存
methodCache.put(method, mapperMethod);
}
// 返回
return mapperMethod;
}
}
MapperMethod.java
package org.apache.ibatis.binding;
import org.apache.ibatis.annotations.Flush;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ParamNameResolver;
import org.apache.ibatis.reflection.TypeParameterResolver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
// 这个类可就厉害了 这是最外围的类 这里就是封装了咱们应用 SqlSession 的操作
public class MapperMethod {
// Sql 标签的类型 Insert Update Delete Select
private final SqlCommand command;
// 办法的参数信息 返回信息等
private final MethodSignature method;
// 结构
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
// 这里就是封装了 SqlSession 的一系列办法 selectOne、select、insert、delete 等
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
// 为什么咱们常常说 Insert update delete 三个标签其实性能一样
// 平时只是语义上有辨别
// 咱们能够点进 sqlSession 源码看看 最初都是调用了 update 办法
case INSERT: {
// 解决参数
Object param = method.convertArgsToSqlCommandParam(args);
// 调用 sqlSessioninsert
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;
}
// 如果是 Select 那就多了
case SELECT:
// 如果返回类型 void 并且有自定义 ResultHandler
if (method.returnsVoid() && method.hasResultHandler()) {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()) {
// 返回 Cursor
result = executeForCursor(sqlSession, args);
} else {
// 返回单个
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
// 清空缓存
result = sqlSession.flushStatements();
break;
default:
// 这个个别不呈现 除非你是个傻子 哈哈
throw new BindingException("Unknown execution method for:" + command.getName());
}
// 这里很有意思
// 咱们可能遇到过 查问后果是根底类型 (boolean、char、byte、short、int、long、float、double) 的话 很容易报空异样
// 咱们写代码肯定留神了 根底类型肯定要保障有返回值 否则你就用封装类型 Integer Double 等
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;
}
// insert update delete 返回解决 rowCount 是 Sqlsession 执行完返回的受影响行数
private Object rowCountResult(int rowCount) {
final Object result;
// 如果返回类型是 void 就间接返回空
if (method.returnsVoid()) {
result = null;
// 返回类型 Integer int
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
result = rowCount;
// 返回类型 Long long
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {result = (long)rowCount;
// 返回类型 Boolean boolean
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {result = rowCount > 0;} else {
// 其余返回类型 间接抛异样
throw new BindingException("Mapper method'" + command.getName() + "'has an unsupported return type:" + method.getReturnType());
}
return result;
}
// 有自定义的 ResuleHandler
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
//
if (void.class.equals(ms.getResultMaps().get(0).getType())) {throw new BindingException("method" + command.getName()
+ "needs either a @ResultMap annotation, a @ResultType annotation,"
+ "or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
}
//
Object param = method.convertArgsToSqlCommandParam(args);
//
if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);
sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
} else {
//
sqlSession.select(command.getName(), param, method.extractResultHandler(args));
}
}
// 多条返回后果
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
// 返回值
List<E> result;
// 把参数转换为 ParamMap
Object param = method.convertArgsToSqlCommandParam(args);
// 是否有分页参数
if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
// 间接执行 sqlSession 的 selectList
result = sqlSession.<E>selectList(command.getName(), param);
}
// class1.isAssignableFrom(class2)
// 判断 class2 是否是 class1 的子类或者子接口
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
// 如果返回类型是 Array 转换为 Array
if (method.getReturnType().isArray()) {return convertToArray(result);
} else {
// 否者转换为申明的汇合汇合
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
// 返回 Cursor
private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
Cursor<T> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<T>selectCursor(command.getName(), param, rowBounds);
} else {result = sqlSession.<T>selectCursor(command.getName(), param);
}
return result;
}
// 转换成汇合
private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
// 先创立一个申明的汇合类型的对象
Object collection = config.getObjectFactory().create(method.getReturnType());
// 转换为代理
MetaObject metaObject = config.newMetaObject(collection);
// 元素都放进去
metaObject.addAll(list);
return collection;
}
// 转换成数组
@SuppressWarnings("unchecked")
private <E> Object convertToArray(List<E> list) {
// 创立数组对象
Class<?> arrayComponentType = method.getReturnType().getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
// 判断是不是根底类型数组 int[] longp[]
if (arrayComponentType.isPrimitive()) {
// 如果是根底类型须要一个一个转换
for (int i = 0; i < list.size(); i++) {
//
Array.set(array, i, list.get(i));
}
return array;
} else {
// 如果不是间接调用 toArray 转换为 Array
return list.toArray((E[])array);
}
}
// 返回 Map
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
Map<K, V> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else {result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
}
return result;
}
// 自定义 Map
// 咱们业务中也能够参考这种写法 就是重写了 get 办法,如果没有获取元素就抛异样
public static class ParamMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -2212268410512043556L;
@Override
public V get(Object key) {if (!super.containsKey(key)) {throw new BindingException("Parameter'" + key + "'not found. Available parameters are" + keySet());
}
return super.get(key);
}
}
}
// 封装了具体执行的动作
public static class SqlCommand {
// xml 的 id 比方:list
private final String name;
// insert update delete 等类型
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
// 名称 list
final String methodName = method.getName();
// 类 dao.TestMapper
final Class<?> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
// 是否有 Flush 标签
if (method.getAnnotation(Flush.class) != null) {
name = null;
// 设置类型为 Flush
type = SqlCommandType.FLUSH;
} else {throw new BindingException("Invalid bound statement (not found):"
+ mapperInterface.getName() + "." + methodName);
}
} else {name = ms.getId();
type = ms.getSqlCommandType();
// 类型不辨认 间接抛异样 INSERT, UPDATE, DELETE, SELECT, FLUSH;
if (type == SqlCommandType.UNKNOWN) {throw new BindingException("Unknown execution method for:" + name);
}
}
}
public String getName() {return name;}
public SqlCommandType getType() {return type;}
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
// statementId ==> dao.TestMapper.list
String statementId = mapperInterface.getName() + "." + methodName;
// 如果曾经有了 间接返回
if (configuration.hasStatement(statementId)) {return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {return null;}
for (Class<?> superInterface : mapperInterface.getInterfaces()) {if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {return ms;}
}
}
return null;
}
}
public static class MethodSignature {
// 是否返回多条后果
private final boolean returnsMany;
// 是否返回 Map
private final boolean returnsMap;
// 是否返回 void
private final boolean returnsVoid;
// 是否返回 Cursor
private final boolean returnsCursor;
// 返回类型
private final Class<?> returnType;
// mapKey
private final String mapKey;
// resultHandler 类型参数的地位
private final Integer resultHandlerIndex;
// rowBound 类型参数的地位
private final Integer rowBoundsIndex;
// 参数处理器
private final ParamNameResolver paramNameResolver;
}
三、唠唠
探险的最初后果是我蒙了,看着看着,看得我心灰意冷了。
这个架构太牛了,各种封装,各种模式,各种
最次要的是咱们看到了,是用了代理模式和工厂模式,把咱们的 Mapper 接口进行了代理,
咱们通过 getMapper 获取的接口其实就是代理对象,这时候所有操作都是通过代理MapperProxy
实现了InvocationHandler 进行的 Jdk 动静代理
我曾经是第二次看源码了,仍旧是不那么清朗,所以咱们没必要一次把所有的点都把握,先把握一个小点儿,比方先理解怎么通过动静代理实现的 Mapper 接口的代理,至于其余的代理的具体内容,再缓缓聊。
欢送关注公众号:木子的昼夜编程