博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring与Mybatis整合的MapperScannerConfigurer
阅读量:6220 次
发布时间:2019-06-21

本文共 3957 字,大约阅读时间需要 13 分钟。

hot3.png

本文将分析mybatis与spring整合的MapperScannerConfigurer的底层原理,之前已经分析过中实现,可以使用jdk自带api和cglib第三方库生成动态代理。本文分析的mybatis版本3.2.7,mybatis-spring版本1.2.2。

MapperScannerConfigurer介绍

MapperScannerConfigurer是spring和mybatis整合的mybatis-spring jar包中提供的一个类。

想要了解该类的作用,就得先了解MapperFactoryBean。

MapperFactoryBean的出现为了代替使用SqlSessionDaoSupport或SqlSessionTemplate编写数据访问对象(DAO)的代码,使用动态代理实现。

比如下面这个官方文档中的配置:

org.mybatis.spring.sample.mapper.UserMapper是一个,我们创建一个MapperFactoryBean实例,然后注入这个接口和sqlSessionFactory(mybatis中提供的SqlSessionFactory接口,MapperFactoryBean会使用SqlSessionFactory创建SqlSession)这两个属性。

之后想使用这个UserMapper接口的话,直接通过spring注入这个bean,然后就可以直接使用了,spring内部会创建一个这个接口的动态代理。

当发现要使用多个MapperFactoryBean的时候,一个一个定义肯定非常麻烦,于是mybatis-spring提供了MapperScannerConfigurer这个类,它将会查找类路径下的映射器并自动将它们创建成MapperFactoryBean。

这段配置会扫描org.mybatis.spring.sample.mapper下的所有接口,然后创建各自接口的动态代理类。

MapperScannerConfigurer底层代码分析

以以下代码为示例进行讲解(部分代码,其他代码及配置省略):

package org.format.dynamicproxy.mybatis.dao;public interface UserDao {    public User getById(int id);    public int add(User user);        public int update(User user);        public int delete(User user);        public List
getAll(); }

我们先通过测试用例debug查看userDao的实现类到底是什么。

07104152_sn38.png
我们可以看到,userDao是1个MapperProxy类的实例。
看下MapperProxy的源码,没错,实现了InvocationHandler,说明使用了jdk自带的动态代理。

public class MapperProxy
implements InvocationHandler, Serializable { private static final long serialUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class
mapper; private final Map
methodCache; public MapperProxy(SqlSession sqlSession, Class
mapperInterface, Map
methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } 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; }}

下面开始分析MapperScannerConfigurer的源码

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口是一个可以修改spring工长中已定义的bean的接口,该接口有个postProcessBeanDefinitionRegistry方法。

07104152_KEkl.png

然后我们看下ClassPathMapperScanner中的关键是如何扫描对应下的接口的。

07104153_ib7q.png

其实MapperScannerConfigurer的作用也就是将对应的接口的类型改造为MapperFactoryBean,而这个MapperFactoryBean的属性mapperInterface是原类型。MapperFactoryBean本文开头已分析过。

所以最终我们还是要分析MapperFactoryBean的实现原理!

MapperFactoryBean继承了SqlSessionDaoSupport类,SqlSessionDaoSupport类继承DaoSupport抽象类,DaoSupport抽象类实现了InitializingBean接口,因此实例个MapperFactoryBean的时候,都会调用InitializingBean接口的afterPropertiesSet方法。

DaoSupport的afterPropertiesSet方法:

07104153_XbMZ.png
MapperFactoryBean重写了checkDaoConfig方法:
07104153_gEXu.png
然后通过spring工厂拿对应的bean的时候:
07104153_2bBq.png
这里的SqlSession是SqlSessionTemplate,SqlSessionTemplate的getMapper方法:
07104154_FBuN.png
Configuration的getMapper方法,会使用MapperRegistry的getMapper方法:
07104154_cxor.png
MapperRegistry的getMapper方法:
07104154_uUTr.png
MapperProxyFactory构造MapperProxy:
07104154_QBcP.png
没错! MapperProxyFactory就是使用了jdk组带的Proxy完成动态代理。
MapperProxy本来一开始已经提到。MapperProxy内部使用了MapperMethod类完成方法的调用:
07104155_EEPs.png

下面,我们以UserDao的getById方法来debug看看MapperMethod的execute方法是如何走的。

@Testpublic void testGet() {    int id = 1; .out.println(userDao.getById(id));}

07104155_rWQf.png

07104155_ekUs.png

示例代码:https://github.com/fangjian0423/dynamic-proxy-mybatis-study

总结

来到了新公司,接触了Mybatis,以前接触过~ 但是接触的不深入,突然发现spring与mybatis整合之后可以只写个接口而不实现,spring默认会帮我们实现,然后觉得非常神奇,于是写了篇java动态代码浅析和本文。

转载于:https://my.oschina.net/tantexian/blog/654043

你可能感兴趣的文章
最严新规发布 网络短视频平台该如何降低违规风险?
查看>>
我的友情链接
查看>>
“不是不需要运维工程师,是人人皆是运维”|对话阿里云MVP蒋烁淼(上)
查看>>
我的友情链接
查看>>
Linux服务器环境下Apache性能优化的技巧
查看>>
解读 KubeCon EU 2019 应用管理领域的新看点
查看>>
标准MFC WinSock ActiveX控件开发实例(基础篇)
查看>>
关于网站404页面产生302状态的问题
查看>>
nginx日志分析
查看>>
Could not reliably determine the server's fully
查看>>
帧中继下的ospf配置
查看>>
解决Fiddler无法抓到手机的会话包
查看>>
Python模块整理(六):守护进程
查看>>
Windows下为PHP安装redis扩展
查看>>
使用Windows计数器
查看>>
IAR中创建STM32工程步骤(寄存器版本)
查看>>
Metro开发中,C#的工程如何打印它所包含的c++的dll的log信息
查看>>
nginx负载均衡策略和配置
查看>>
认识计算机的硬件配备
查看>>
关于Boot
查看>>