mybatis plugins_30.MyBatis插件原理与Spring集成

目录

  1. 插件使用与原理
  2. 编写自定义插件
  3. 掌握Spring集成myBatis原理

1.插件使用与原理

1.1.插件使用

编写插件

编写拦截器类,以PageHelper为例

1)实现Interceptor接口

public 

2)实现方法。intercept就是拦截方法,增强代码写里面。

v2-18e664cdc4cb9c7773d391034a342152_b.jpg

3)在拦截器类上加上参数。注解签名注明拦截对象、拦截方法、拦截方法参数。

下面拦截Executor中的两个query方法。

@Intercepts

插件配置

在mybatis-config.xml注册插件,配置属性

<!--分页插件的注册-->
 

解析注册插件

myBatis启动时扫描<plugins>标签,注册到Configuration对象的InterceptorChain中。通过setProperties将参数放到property里。

XMLConfigBuilder类

private 

解析时将所有插件存到Configuration的InterceptorChain中,它是list类型。

public 

1.2.插件理解

不修改代码怎么增强功能?

代理模式,myBatis插件实现原理

多个插件怎么拦截?

责任链模式,链路执行,层层拦截。

什么对象可以被拦截?

有哪些对象和方法可以被拦截?

https://mybatis.org/mybatis-3/zh/configuration.html#plugins

下面两张图:

v2-99354d51dd45808046edcb2dbe09f376_b.jpg

v2-73bfe13580246adf3107f6f382bcb778_b.jpg

Executor有可能被二级缓存装饰。

Executor会拦截CachingExecutor或BaseExecutor。

DefaultSqlSessionFactory.openSessionFromDataSource():

v2-8f68e14bc803c3b5ab85d2f82f1246b3_b.jpg

先创建基本类型,再二级缓存装饰,最后插件拦截。

所以拦截的是CachingExecutor。

1.3.插件实现原理

代理模式,需要解决的问题:

1.代理类怎么创建?

2.什么时候创建?

3.调用流程什么样?

代理类什么时候创建?

Executor拦截代理类是openSession时创建

怎么创建?

遍历InterceptorChain,使用Interceptor实现类的plugin方法,对目标对象进行代理。

public 
@Override
 

这个plugin方法是自己实现的,返回一个代理对象。

JDK动态代理,需要实现InvocationHandler接口触发管理类。

用Proxy.nexProxyInstance创建对象。

myBatis插件机制将这些类封装好了,提供了一个触发管理类Plugin,

实现了InvocationHandler。

创建代理对象的newProxyInstance方法也进行了封装,就是wrap。

public 

被代理后的调用流程

先触发管理类Plugin的invoke方法

@Override
 

如果被拦截方法不为空,进入Plugin的invoke方法,调用interceptor的intercept方法,到我们自己实现的拦截逻辑。

return 

new Invocation(target, method, args) 对象是对被拦截对象、方法、参数的封装。

被代理对象执行它的方法从Invocation对象拿。

总结:

v2-29eba52a245fca548c26aa853ae5b576_b.jpg

配置顺序与执行顺序?

配置与执行顺序是相反的。

InterceptorChain从上往下添加,执行从最后开始。

v2-d68dbb4e95b07a4adfb91e88be262718_b.jpg

总结

v2-5fe1135e3e08be38cad353d59c05084e_b.png

1.4.PageHelper原理

翻页

使用RowBounds翻页,在内存中筛选数据。

使用

public 

原理

拦截器类PageInterceptor。

先判断是否需要count获得总数,默认true。

获得count后,判断是否需要分页,如果pageSize>0,就分页。

下面通过getPageSql方法生成新BoundSql:

v2-0139c0ea3106de1a993a2037fb6f5a3f_b.png

getPageSql对不同数据库有不同实现

v2-7ccc1640c3ab4d5c99e9a01d49a6ee48_b.jpg

实际是添加了LIMIT语句,加上了起始与结束。

v2-4846734eac20b6ecdab2d381c39cd28f_b.jpg

插件是如何获取页码和每页数量?

PageHelper.startPage方法,调用了PageMethod的setLocalPage方法,包装了一个Page对象,并且把对象放到ThreadLocal中。

v2-7442e0b9031733f48ebae0efae5aaa7b_b.png

AbstractHelperDialect中,Page对象中的翻页信息是通过getLocalPage()取出的:

v2-5d50b29213e833aed35fd52f27e33d55_b.jpg

调的就是PageHelper的getLocalPage,从ThreadLocal中获取到

v2-32873788ebf6ef4dec9b3783daabbf75_b.png

每次查询(每个线程)都有线程私有Page对象,里面有页码和每页数量。

关键类

v2-e164f0590f3c8bb33da9ce2dc7318131_b.jpg

1.5.应用场景分析

v2-9d13aaea9dbaaae1dc78f74c2c8de004_b.png

v2-8245e1ad9830235e8ab1e7b50924dfa4_b.jpg

2.与Spring 整合分析

2.1.关键配置

pom依赖

出了mybatis依赖,还需要mybais和spring整合包。

叫mybatis-spring。版本要对应。

<!--mybatis 和Spring整合 -->
 

SqlSessionFactoryBean

applicationContext.xml配置这个类。

这个Bean会初始化SqlSessionFactory,用来创建SqlSession。

属性要指定mybatis-config.xml和Mapper映射器文件。

<!-- 在Spring启动时创建 sqlSessionFactory -->
 

MapperScannerConfigurer

applicationContext.xml配置扫描Mapper接口路径。

方法一:

<!--配置扫描器,将mybatis的接口实现加入到  IOC容器中  -->
 

方法二:

<bean 

方法三:

v2-f632f8021ea053f4d645adb149208eaf_b.jpg

Spring集成mybatis原理:

1)SqlSessionFactory在哪创建?

2)SqlSession在哪创建?

3)代理类在哪创建?

2.2.创建会话工厂SqlSessionFactory

v2-076e23e7f551abc5d772d6352071d5e4_b.png

实现了三个接口:InitializingBean、FactoryBean、ApplicationListener

v2-2f931155e24fff54821746e8271b0c50_b.jpg

InitializingBean

实现AfterPropertiesSet方法,在bean属性值设置完后调用

v2-700c69b0ed8b3f792c72722a1e7071e7_b.jpg

调用了buildSqlSesssionFactory方法。

创建Configuration对象。

创建解析全局配置文件XMLConfigBuilder。

v2-cde1ec26fc279e05d0d1595fea832c09_b.png

FactoryBean接口

让用户自定义实例化Bean逻辑。

获取SqlSessionFactoryBean,就会调用它的getObject方法。

getObject方法调用了afterPropertiesSet方法,做mybatis解析配置,返回DefaultSqlSessionFactory。

v2-4e1618b6264647059c83cae1aae6c64d_b.jpg

ApplicationListener

监听ContextRefreshedEven(上下文刷新实践),会在SPring容器加载完后执行。

检查ms是否加载完毕。

v2-1d01331c5e8a438bb389063ea867ffde_b.jpg

SqlSessionFactoryBean用到的Spring扩展点

v2-6822d9a33e2b2106147a86926f6b964f_b.png

2.3.创建会话SqlSession

DefaultSqlSession是线程不安全的。

v2-5ff650dd0eaa2efa7ef425bc311025aa_b.jpg

mybatis-spring包,提供了线程安全的SqlSession包装类,SqlSessionTemplate。

可以在所有DAO层共享实例(默认单例)

v2-0105b8b218fd0edeb3dbdd0bb005a017_b.png

SqlSessionTemplate,增删改查都是调用代理对象的方法。

v2-d9351c6d7b74c6f9d05ffd49466872e7_b.png

代理对象在构造方法通过JDK动态代理创建:

v2-65f3d3f916cf7f636ed4edb4f5d68e13_b.png

怎么拿到一个SqlSessionTemplate?

提供抽象支持类SqlSessionDaoSupport,持有一个SqlSessionTemplate对象,提供getSqlSession方法。

v2-87f810be01116dd863b70852cfcef93d_b.jpg

在实现类得方法里,可以直接调用父类封装的selectOne方法,

最终会调用sqlSessionTemplate的selectOne方法。

v2-85b00c37f28d01d91ce9cf41d2d4c4ad_b.jpg

2.4.接口的扫描注册

MapperScannerConfigurer用来扫描Mapper接口的。

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口。

BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor子类,里面有一个postProcessBeanDefintionRegistry方法。

v2-63dd00acb27d93a943d5f00865145fee_b.jpg

MapperScannerConfigurer重写了postProcessBeanDefintionRegistry方法。

创建了scanner对象,设置属性。

2.5.接口注入使用

Spring如何把 mybatis集成进去?

1.提供SqlSession替代品SqlSessionTemplate,里面有一个实现InvocationHandler的内部SqlSessionInterceptor,本质是对SqlSession的代理.

2.提供获取SqlSessionTemplate的抽象类SqlSessionDaoSupport

3.扫描Mapper接口,注册到容器中的是MapperFactoryBean

4.把Mapper注入使用的时候,调用的是getObject方法

5.执行Mapper接口任意方法,会走到触发管理类MapperProxy,进去SQL处理流程

学到了?

1.为组件预留扩展接口

2.利用Spring扩展机制,把组件集成到mybatis中

v2-ae4b19503db1f0b3723824af6fd4bbea_b.jpg

设计模式总结:

v2-61374a74643f595d686303266e5093bf_b.jpg

参考资料:

1.咕泡学院·MyBatis插件原理与Spring集成·青山

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值