镇江网站开发公司,建设部网站怎么查询相关专业,郑州做网站的专业公司有哪些,网站栏目代码文章目录 前言Mybatis dao层两种实现方式的对比原始Dao开发原始Dao开发的弊端 基于Mapper动态代理的开发方式 Mybatis动态代理实现方式的原理解析动态代理调用链路解析先给出链路调用结果1、调用方法的开始#xff1a;session.getMapper2、DeaultSqlSession的getMapper3、Conf… 文章目录 前言Mybatis dao层两种实现方式的对比原始Dao开发原始Dao开发的弊端 基于Mapper动态代理的开发方式 Mybatis动态代理实现方式的原理解析动态代理调用链路解析先给出链路调用结果1、调用方法的开始session.getMapper2、DeaultSqlSession的getMapper3、Configuration的getMapper4、MapperRegistry的getMapper5、MapperProxyFactory的newIntance6、MapperProxy的invoke7、MapperMethod的execute 动态代理类的接口注册/生成先给出链路调用结果1、SqlSessionFactoryBuilder().build全局配置文件解析2、XMLConfigBuilder#parse--parseConfiguration4、映射器Mapper文件的解析XMLConfigBuilder#mapperElement5、XMLMapperBuilder#parse6、XMLMapperBuilder#configurationElement(XNode context)7、XMLMapperBuilder#bindMapperForNamespace()核心方法8、MapperRegistry#addMappper() 总结 前言 提到MyBatis很多人可能已经使用过MyBatis中的mapper接口实际上并没有对应的实现类它的功能通过一个对应的xml配置文件来实现。这意味着当我们调用一个mapper接口时我们实际上是在执行xml文件中定义的SQL语句来操作数据。那么Mybatis的mapper为啥只有接口没有实现类它却能工作答案很简单动态代理但是要真正理解这个动态代理的整个过程还是有点费劲的没事接下来我们一步步解析。
Mybatis dao层两种实现方式的对比
我们先把刚开始学习 MyBatis 的两种开发方式都回顾一下虽然我们说回头都是用 Mapper 接口动态代理开发但原始 Dao 开发的方式也不要忘记这种方式在以后的开发中可能还是用得上的。另外通过对比我们自己实现dao层接口以及mybatis动态代理可以更加直观的展现出mybatis动态代理替我们所做的工作有利于我们理解动态代理的过程。
原始Dao开发
DepartmentDao接口
public interface DepartmentDao {ListDepartment findAll();Department findById(String id);
}DepartmentDaoImpl: 注意这里的关键代码 sqlSession.selectList(com.linkedbear.mybatis.mapper.DepartmentMapper.findAll)需要我们自己手动调用SqlSession里面的方法基于动态代理的方式最后的目标也是成功的调用到这里。 注意如果是添加更新或者删除操作的话需要在方法中增加事务的提交。
public class DepartmentDaoImpl implements DepartmentDao {private SqlSessionFactory sqlSessionFactory;public DepartmentDaoImpl(SqlSessionFactory sqlSessionFactory) {this.sqlSessionFactory sqlSessionFactory;}Overridepublic ListDepartment findAll() {//使用了 try-with-resource 的方式可以省略 sqlSession.close();的代码。try (SqlSession sqlSession sqlSessionFactory.openSession()) {return sqlSession.selectList(com.linkedbear.mybatis.mapper.DepartmentMapper.findAll);}}Overridepublic Department findById(String id) {try (SqlSession sqlSession sqlSessionFactory.openSession()) {return sqlSession.selectOne(com.linkedbear.mybatis.mapper.DepartmentMapper.findById, id);}}
}departmentMapper.xml
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacedepartmentMapperselect idfindAll resultTypecom.linkedbear.mybatis.entity.Departmentselect * from tbl_department/selectselect idfindById parameterTypestring resultTypecom.linkedbear.mybatis.entity.Departmentselect * from tbl_department where id #{id}/select
/mapperMyBatisApplication 测试运行
public class MyBatisApplication {public static void main(String[] args) throws Exception {InputStream xml Resources.getResourceAsStream(mybatis-config.xml);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(xml);DepartmentDao departmentDao new DepartmentDaoImpl(sqlSessionFactory);ListDepartment departmentList departmentDao.findAll();departmentList.forEach(System.out::println);}
}原始Dao开发的弊端
Override
public ListDepartment findAll() {try (SqlSession sqlSession sqlSessionFactory.openSession()) {return sqlSession.selectList(com.linkedbear.mybatis.mapper.DepartmentMapper.findAll);}
}Override
public Department findById(String id) {try (SqlSession sqlSession sqlSessionFactory.openSession()) {return sqlSession.selectOne(com.linkedbear.mybatis.mapper.DepartmentMapper.findById, id);}
}从上面的编码中我们会发现接口中存在好多重复代码
可以发现两个方法的方法名不同、参数列表不同调用的 mapper 不同返回值不同其余的几乎完全相同我们也知道更好地优化方案是使用 Mapper 动态代理的方式。所以下面我们再接下来重点讲解使用 Mapper 动态代理的方式开发 Dao 层及其原理。
基于Mapper动态代理的开发方式
使用 Mapper 动态代理的方式开发需要满足以下几个规范
mapper.xml 中的 namespace 与 Mapper 接口的全限定名完全相同mapper.xml 中定义的 statement 其 id 与 Mapper 接口的方法名一致Mapper 接口方法的方法参数类型与 mapper.xml 中定义的 statement 的 parameterType 类型一致Mapper 接口方法的返回值类型与 mapper.xml 中定义的 statement 的 resultType 类型相同
使用动态代理的话Dao层的接口声明完成以后只需要在使用的时候通过SqlSession对象的getMapper方法获取对应Dao接口的代理对象关键代码如下
获取到dao层的代理对象以后通过代理对象调用查询方法就可以实现查询所有部门列表的功能。
public class MyBatisApplication {public static void main(String[] args) throws Exception {InputStream xml Resources.getResourceAsStream(mybatis-config.xml);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(xml);SqlSession sqlSession sqlSessionFactory.openSession();// 获取Mapper接口的代理DepartmentMapper departmentMapper sqlSession.getMapper(DepartmentMapper.class);Department department departmentMapper.findById(18ec781fbefd727923b0d35740b177ab);System.out.println(department);}
}Mybatis动态代理实现方式的原理解析
Mybatis的动态代理工作原理概括步骤如下
Mapper接口与XML的关联 Mybatis初始化时会解析XML配置文件将里面定义的SQL语句与Mapper接口的方法建立映射关系并保存在配置对象中。 动态代理的创建 当我们调用SqlSession.getMapper()方法时Mybatis使用Java动态代理机制为Mapper接口创建代理对象。代理对象的创建主要通过MapperProxyFactory类来完成。 调用代理对象的方法时的内部处理 当调用Mapper接口中的方法时实际上调用的是代理对象的invoke方法。在invoke方法中Mybatis使用之前的映射关系找到与方法对应的SQL语句。 SQL语句的执行 找到SQL语句后Mybatis会调用底层的执行器Executor来执行SQL语句并完成参数的绑定查询以及结果返回。
动态代理调用链路解析 注意这里先从我们的使用开始讲解它是如何被调用的后面再解析动态代理类的接口的注册 动态代理中最重要的类Configuration、SqlSession、MapperRegistry、MapperProxyFactory、MapperProxy、MapperMethod下面开始从入口方法到调用结束的过程分析。
先给出链路调用结果
getMapper方法的大致调用逻辑链是 SqlSession#getMapper()--DeaultSqlSession#getMapper—— Configuration#getMapper() —— MapperRegistry#getMapper() —— MapperProxyFactory#newInstance() —— Proxy#newProxyInstance()--MapperProxy#invoke--MapperMethod#execute
1、调用方法的开始session.getMapper
UserDao mapper session.getMapper(UserDao.class); //因为SqlSesseion为接口所以我们通过Debug方式发现这里使用的实现类为DefaultSqlSession。2、DeaultSqlSession的getMapper
找到DeaultSqlSession中的getMapper方法发现这里没有做其他的动作只是将工作继续抛到了Configuration类中Configuration为类不是接口可以直接进入该类的getMapper方法中
Overridepublic T T getMapper(ClassT type) {return configuration.TgetMapper(type, this);}3、Configuration的getMapper
找到Configuration类的getMapper方法这里也是将工作继续交到MapperRegistry的getMapper的方法中所以我们继续向下进行。 MapperRegistry还有一个方法是public T void addMapper(ClassT type) 后面再进行解析 public T T getMapper(ClassT type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}4、MapperRegistry的getMapper
4、找到MapperRegistry的getMapper的方法,看到这里发现和以前不一样了通过MapperProxyFactory的命名方式我们知道这里将通过这个工厂生成我们所关注的MapperProxy的代理类然后我们通过mapperProxyFactory.newInstance(sqlSession);进入MapperProxyFactory的newInstance方法中 knownMappers注意这个后面会解析这个knownMappers 是怎么来的、如何使用的。 private final MapClass?, MapperProxyFactory? knownMappers new HashMap(); public T T getMapper(ClassT type, SqlSession sqlSession) {//根据Class对象获取创建动态代理的工厂对象MapperProxyFactoryfinal MapperProxyFactoryT mapperProxyFactory (MapperProxyFactoryT) 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);}}5、MapperProxyFactory的newIntance
找到MapperProxyFactory的newIntance方法通过参数类型SqlSession可以得知上面的调用先进入第二个newInstance方法中并创建我们所需要重点关注的MapperProxy对象第二个方法中再调用第一个newInstance方法并将MapperProxy对象传入进去根据该对象创建代理类并返回。这里已经得到需要的代理类了但是我们的代理类所做的工作还得继续向下看MapperProxy类。
protected T newInstance(MapperProxyT mapperProxy) {//这里使用JDK动态代理通过Proxy.newProxyInstance生成动态代理类// newProxyInstance的参数类加载器、接口类、InvocationHandler接口实现类// 动态代理可以将所有接口的调用重定向到调用处理器InvocationHandler调用它的invoke方法return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}public T newInstance(SqlSession sqlSession) {final MapperProxyT mapperProxy new MapperProxyT(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}6、MapperProxy的invoke
找到MapperProxy类发现其确实实现了JDK动态代理必须实现的接口InvocationHandler所以我们重点关注invoke()方法这里看到在invoke方法里先获取MapperMethod类然后调用mapperMethod.execute()所以我们继续查看MapperMethod类的execute方法。
public class MapperProxyT implements InvocationHandler, Serializable {public MapperProxy(SqlSession sqlSession, ClassT mapperInterface, MapMethod, MapperMethod methodCache) {this.sqlSession sqlSession;this.mapperInterface mapperInterface;this.methodCache methodCache;}Overridepublic 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);}//调用XxxMapper接口自定义的方法进行代理//首先将当前被调用的方法Method构造成一个MapperMethod对象然后掉用其execute方法真正的开始执行。final MapperMethod mapperMethod cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}}7、MapperMethod的execute
找到类MapperMethod类的execute方法发现execute中通过调用本类中的其他方法获取并封装返回结果我们来看一下MapperMethod整个类。
public Object execute(SqlSession sqlSession, Object[] args) {Object result;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);}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;}MapperMethod类是整个代理机制的核心类对SqlSession中的操作进行了封装使用。 该类里有两个内部类SqlCommand和MethodSignature。 SqlCommand用来封装CRUD操作也就是我们在xml中配置的操作的节点。每个节点都会生成一个MappedStatement类。MethodSignature用来封装方法的参数以及返回类型在execute的方法中我们发现在这里又回到了SqlSession中的接口调用和我们自己实现UerDao接口的方式中直接用SqlSession对象调用DefaultSqlSession的实现类的方法是一样的经过一大圈的代理又回到了原地这就是整个动态代理的实现过程了。
sqlSession.selectList(com.linkedbear.mybatis.mapper.DepartmentMapper.findAll);回忆一下上面的解析过程是不是就是一开始给出的链路调用流程
getMapper方法的大致调用逻辑链是 SqlSession#getMapper() —— Configuration#getMapper() —— MapperRegistry#getMapper() —— MapperProxyFactory#newInstance() —— Proxy#newProxyInstance()–MapperProxy#invoke–MapperMethod#execute
还有一点我们需要注意我们通过SqlSession的getMapper方法获得接口代理来进行CRUD操作其底层还是依靠的是SqlSession的使用方法。
动态代理类的接口注册/生成 刚刚我先讲解了动态代理调用链路是怎么样的但是刚刚上面步骤3、4中涉及的两个点我这里再进行全面讲解 Configuration中两个重要方法getMapper()和addMapper()–实际实现是MapperRegistry刚刚讲解了getMapper()的链路流程接下来讲解addMapper()
getMapper(): 用于创建接口的动态类addMapper(): mybatis在解析配置文件时会将需要生成动态代理类的接口注册到其中
public class MyBatisApplication {public static void main(String[] args) throws Exception {InputStream xml Resources.getResourceAsStream(mybatis-config.xml);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(xml);SqlSession sqlSession sqlSessionFactory.openSession();// 获取Mapper接口的代理DepartmentMapper departmentMapper sqlSession.getMapper(DepartmentMapper.class);Department department departmentMapper.findById(18ec781fbefd727923b0d35740b177ab);System.out.println(department);}
}在之前的实例中我们通过调用sqlSession.getMapper()方法获得了DepartmentMapper接口的一个实例。实际上通过这个方法我们得到的是DepartmentMapper接口的一个动态代理实现然后我们可以借助这个动态代理实现来调用方法。在揭秘这些动态代理是如何创建出来的之前 让我们先来审视一下SqlSessionFactory工厂的建立过程以及它是如何处理相关的配置如mybatis-config文件以及它是如何加载映射文件的。
先给出链路调用结果
new SqlSessionFactoryBuilder().build(xml)--XMLConfigBuilder#parse--XMLConfigBuilder#parseConfiguration---XMLConfigBuilder#mapperElement--XMLMapperBuilder#mapperParser.parse()--XMLMapperBuilder#configurationElement--XMLMapperBuilder#bindMapperForNamespace--Configuration#MapperRegistry#addMappper()
1、SqlSessionFactoryBuilder().build全局配置文件解析
private static SqlSessionFactory sqlSessionFactory;
static {try {sqlSessionFactory new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(mybatis-config.xml));} catch (IOException e) {e.printStackTrace();}
}
我们使用new SqlSessionFactoryBuilder().build()的方式创建SqlSessionFactory工厂走进build方法 public SqlSessionFactory build(InputStream inputStream, Properties properties) {return build(inputStream, null, properties);}public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {XMLConfigBuilder parser new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException(Error building SqlSession., e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}
2、XMLConfigBuilder#parse–parseConfiguration
对于mybatis的全局配置文件的解析相关解析代码位于XMLConfigBuilder的parse()方法中 public Configuration parse() {if (parsed) {throw new BuilderException(Each XMLConfigBuilder can only be used once.);}parsed true;//解析全局配置文件parseConfiguration(parser.evalNode(/configuration));return configuration;}private void parseConfiguration(XNode root) {try {//issue #117 read properties firstpropertiesElement(root.evalNode(properties));Properties settings settingsAsProperties(root.evalNode(settings));loadCustomVfs(settings);loadCustomLogImpl(settings);typeAliasesElement(root.evalNode(typeAliases));pluginElement(root.evalNode(plugins));objectFactoryElement(root.evalNode(objectFactory));objectWrapperFactoryElement(root.evalNode(objectWrapperFactory));reflectorFactoryElement(root.evalNode(reflectorFactory));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode(environments));databaseIdProviderElement(root.evalNode(databaseIdProvider));typeHandlerElement(root.evalNode(typeHandlers));//解析mapper映射器文件mapperElement(root.evalNode(mappers));} catch (Exception e) {throw new BuilderException(Error parsing SQL Mapper Configuration. Cause: e, e);}}
从parseConfiguration方法的源代码中很容易就可以看出它对mybatis全局配置文件中各个元素属性的解析。当然最终解析后返回一个Configuration对象Configuration是一个很重要的类它包含了Mybatis的所有配置信息它是通过XMLConfigBuilder取钱构建的Mybatis通过XMLConfigBuilder读取mybatis-config.xml中配置的信息然后将这些信息保存到Configuration中
4、映射器Mapper文件的解析XMLConfigBuilder#mapperElement 动态代理类的接口注册/生成就是由这部分实现的 //解析mapper映射器文件
mapperElement(root.evalNode(mappers));该方法是对全局配置文件中mappers属性的解析走进去
private void mapperElement(XNode parent) throws Exception {String resource;.....resource child.getStringAttribute(resource);String url child.getStringAttribute(url);String mapperClass child.getStringAttribute(class);XMLMapperBuilder mapperParser;InputStream inputStream;if (resource ! null url null mapperClass null) {ErrorContext.instance().resource(resource);inputStream Resources.getResourceAsStream(resource);mapperParser new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());mapperParser.parse();
XMLMapperBuilder和刚刚的XMLConfigBuilder是不是看起来很像一个是解析configuration一个是解析mapper
5、XMLMapperBuilder#parse 这里重点关注两个方法configurationElement和bindMapperForNamespace mapperParser.parse()方法就是XMLMapperBuilder对Mapper映射器文件进行解析可与XMLConfigBuilder进行类比 public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode(/mapper)); //解析映射文件的根节点mapper元素configuration.addLoadedResource(resource); bindMapperForNamespace(); //重点方法这个方法内部会根据namespace属性值生成动态代理类}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}
6、XMLMapperBuilder#configurationElement(XNode context)
该方法主要用于将mapper文件中的元素信息比如insert、select这等信息解析到MappedStatement对象并保存到Configuration类中的mappedStatements属性中以便于后续动态代理类执行CRUD操作时能够获取真正的Sql语句信息 private void configurationElement(XNode context) {try {String namespace context.getStringAttribute(namespace);if (namespace ! null !namespace.isEmpty()) {this.builderAssistant.setCurrentNamespace(namespace);this.cacheRefElement(context.evalNode(cache-ref));this.cacheElement(context.evalNode(cache));this.parameterMapElement(context.evalNodes(/mapper/parameterMap));this.resultMapElements(context.evalNodes(/mapper/resultMap));this.sqlElement(context.evalNodes(/mapper/sql));this.buildStatementFromContext(context.evalNodes(select|insert|update|delete));} else {throw new BuilderException(Mappers namespace cannot be empty);}} catch (Exception var3) {throw new BuilderException(Error parsing Mapper XML. The XML location is this.resource . Cause: var3, var3);}}XMLMapperBuilder#buildStatementFromContext方法就用于解析insert、select这类元素信息并将其封装成MappedStatement对象具体的实现细节这里就不细说了。
7、XMLMapperBuilder#bindMapperForNamespace()核心方法
该方法是核心方法它会根据mapper文件中的namespace属性值为接口生成动态代理类这就来到了我们的主题内容——动态代理类是如何生成的。
bindMapperForNamespace方法源码如下所示 private void bindMapperForNamespace() {//获取mapper元素的namespace属性值String namespace builderAssistant.getCurrentNamespace();if (namespace ! null) {Class? boundType null;try {// 获取namespace属性值对应的Class对象boundType Resources.classForName(namespace);} catch (ClassNotFoundException e) {//如果没有这个类则直接忽略这是因为namespace属性值只需要唯一即可并不一定对应一个XXXMapper接口//没有XXXMapper接口的时候我们可以直接使用SqlSession来进行增删改查}if (boundType ! null) {if (!configuration.hasMapper(boundType)) {// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResourceconfiguration.addLoadedResource(namespace: namespace);//如果namespace属性值有对应的Java类调用Configuration的addMapper方法将其添加到MapperRegistry中configuration.addMapper(boundType);}}}}
这里提到了Configuration的addMapper方法实际上Configuration类里面通过MapperRegistry对象维护了所有要生成动态代理类的XxxMapper接口信息可见Configuration类确实是相当重要一类
public class Configuration {...protected MapperRegistry mapperRegistry new MapperRegistry(this);...public T void addMapper(ClassT type) {mapperRegistry.addMapper(type);}public T T getMapper(ClassT type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}...
}
8、MapperRegistry#addMappper()
Configuration将addMapper方法委托给MapperRegistry的addMapper进行的源码如下 public T void addMapper(ClassT type) {// 这个class必须是一个接口因为是使用JDK动态代理所以需要是接口否则不会针对其生成动态代理if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException(Type type is already known to the MapperRegistry.);}boolean loadCompleted false;try {// 生成一个MapperProxyFactory用于之后生成动态代理类knownMappers.put(type, new MapperProxyFactory(type));//以下代码片段用于解析我们定义的XxxMapper接口里面使用的注解这主要是处理不使用xml映射文件的情况MapperAnnotationBuilder parser new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
MapperRegistry内部维护一个映射关系每个接口对应一个MapperProxyFactory生成动态代理工厂类
private final MapClass?, MapperProxyFactory? knownMappers new HashMap();这样便于在后面调用MapperRegistry的getMapper()时直接从Map中获取某个接口对应的动态代理工厂类然后再利用工厂类针对其接口生成真正的动态代理类。
总结
Mybatis的动态代理工作原理概括步骤如下
Mapper接口与XML的关联也就是我们刚刚上面解析的动态代理类的接口注册/生成 Mybatis初始化时会解析XML配置文件将里面定义的SQL语句与Mapper接口的方法建立映射关系并保存在配置对象中。 动态代理的创建2、3、4也就是我们刚刚上面解析的动态代理实际调用的链路流程 当我们调用SqlSession.getMapper()方法时Mybatis使用Java动态代理机制为Mapper接口创建代理对象。代理对象的创建主要通过MapperProxyFactory类来完成。 调用代理对象的方法时的内部处理 当调用Mapper接口中的方法时实际上调用的是代理对象的invoke方法。在invoke方法中Mybatis使用之前的映射关系找到与方法对应的SQL语句。 SQL语句的执行 找到SQL语句后Mybatis会调用底层的执行器Executor来执行SQL语句并完成参数的绑定查询以及结果返回。 参考文章 https://www.cnblogs.com/hopeofthevillage/p/11384848.html