目录
- 插件使用与原理
- 编写自定义插件
- 掌握Spring集成myBatis原理
1.插件使用与原理
1.1.插件使用
编写插件
编写拦截器类,以PageHelper为例
1)实现Interceptor接口
public
2)实现方法。intercept就是拦截方法,增强代码写里面。
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
下面两张图:
Executor有可能被二级缓存装饰。
Executor会拦截CachingExecutor或BaseExecutor。
DefaultSqlSessionFactory.openSessionFromDataSource():
先创建基本类型,再二级缓存装饰,最后插件拦截。
所以拦截的是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对象拿。
总结:
配置顺序与执行顺序?
配置与执行顺序是相反的。
InterceptorChain从上往下添加,执行从最后开始。
总结
1.4.PageHelper原理
翻页
使用RowBounds翻页,在内存中筛选数据。
使用
public
原理
拦截器类PageInterceptor。
先判断是否需要count获得总数,默认true。
获得count后,判断是否需要分页,如果pageSize>0,就分页。
下面通过getPageSql方法生成新BoundSql:
getPageSql对不同数据库有不同实现
实际是添加了LIMIT语句,加上了起始与结束。
插件是如何获取页码和每页数量?
PageHelper.startPage方法,调用了PageMethod的setLocalPage方法,包装了一个Page对象,并且把对象放到ThreadLocal中。
AbstractHelperDialect中,Page对象中的翻页信息是通过getLocalPage()取出的:
调的就是PageHelper的getLocalPage,从ThreadLocal中获取到
每次查询(每个线程)都有线程私有Page对象,里面有页码和每页数量。
关键类
1.5.应用场景分析
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
方法三:
Spring集成mybatis原理:
1)SqlSessionFactory在哪创建?
2)SqlSession在哪创建?
3)代理类在哪创建?
2.2.创建会话工厂SqlSessionFactory
实现了三个接口:InitializingBean、FactoryBean、ApplicationListener
InitializingBean
实现AfterPropertiesSet方法,在bean属性值设置完后调用
调用了buildSqlSesssionFactory方法。
创建Configuration对象。
创建解析全局配置文件XMLConfigBuilder。
FactoryBean接口
让用户自定义实例化Bean逻辑。
获取SqlSessionFactoryBean,就会调用它的getObject方法。
getObject方法调用了afterPropertiesSet方法,做mybatis解析配置,返回DefaultSqlSessionFactory。
ApplicationListener
监听ContextRefreshedEven(上下文刷新实践),会在SPring容器加载完后执行。
检查ms是否加载完毕。
SqlSessionFactoryBean用到的Spring扩展点
2.3.创建会话SqlSession
DefaultSqlSession是线程不安全的。
mybatis-spring包,提供了线程安全的SqlSession包装类,SqlSessionTemplate。
可以在所有DAO层共享实例(默认单例)
SqlSessionTemplate,增删改查都是调用代理对象的方法。
代理对象在构造方法通过JDK动态代理创建:
怎么拿到一个SqlSessionTemplate?
提供抽象支持类SqlSessionDaoSupport,持有一个SqlSessionTemplate对象,提供getSqlSession方法。
在实现类得方法里,可以直接调用父类封装的selectOne方法,
最终会调用sqlSessionTemplate的selectOne方法。
2.4.接口的扫描注册
MapperScannerConfigurer用来扫描Mapper接口的。
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口。
BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor子类,里面有一个postProcessBeanDefintionRegistry方法。
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中
设计模式总结:
参考资料:
1.咕泡学院·MyBatis插件原理与Spring集成·青山