30.MyBatis插件原理与Spring集成
发布网友
发布时间:2024-10-04 12:30
我来回答
共1个回答
热心网友
时间:2024-12-02 22:34
编写插件
编写*类,以PageHelper为例
1)实现Interceptor接口
2)实现方法。intercept就是拦截方法,增强代码写里面。
3)在*类上加上参数。注解签名注明拦截对象、拦截方法、拦截方法参数。
下面拦截Executor中的两个query方法。
插件配置
在mybatis-config.xml注册插件,配置属性
解析注册插件
myBatis启动时扫描标签,注册到Configuration对象的InterceptorChain中。通过setProperties将参数放到property里。
XMLConfigBuilder类
解析时将所有插件存到Configuration的InterceptorChain中,它是list类型。
不修改代码怎么增强功能?
代理模式,myBatis插件实现原理
多个插件怎么拦截?
责任链模式,链路执行,层层拦截。
什么对象可以被拦截?
有哪些对象和方法可以被拦截?
下面两张图:Executor有可能被二级缓存装饰。
Executor会拦截CachingExecutor或BaseExecutor。
DefaultSqlSessionFactory.openSessionFromDataSource():先创建基本类型,再二级缓存装饰,最后插件拦截。所以拦截的是CachingExecutor。
代理模式,需要解决的问题:1.代理类怎么创建?2.什么时候创建?3.调用流程什么样?
代理类什么时候创建?Executor拦截代理类是openSession时创建。
怎么创建?遍历InterceptorChain,使用Interceptor实现类的plugin方法,对目标对象进行代理。
这个plugin方法是自己实现的,返回一个代理对象。
JDK动态代理,需要实现InvocationHandler接口触发管理类。
用Proxy.nexProxyInstance创建对象。
myBatis插件机制将这些类封装好了,提供了一个触发管理类Plugin,实现了InvocationHandler。
创建代理对象的newProxyInstance方法也进行了封装,就是wrap。
被代理后的调用流程:先触发管理类Plugin的invoke方法
如果被拦截方法不为空,进入Plugin的invoke方法,调用interceptor的intercept方法,到我们自己实现的拦截逻辑。
new Invocation(target, method, args) 对象是对被拦截对象、方法、参数的封装。
被代理对象执行它的方法从Invocation对象拿。
配置顺序与执行顺序?配置与执行顺序是相反的。InterceptorChain从上往下添加,执行从最后开始。
总结:配置顺序与执行顺序?配置与执行顺序是相反的。InterceptorChain从上往下添加,执行从最后开始。
PageHelper原理:翻页使用RowBounds翻页,在内存中筛选数据。使用PageInterceptor*类。先判断是否需要count获得总数,默认true。获得count后,判断是否需要分页,如果pageSize>0,就分页。通过getPageSql方法生成新BoundSql:实际是添加了LIMIT语句,加上了起始与结束。
插件是如何获取页码和每页数量?PageHelper.startPage方法,调用了PageMethod的setLocalPage方法,包装了一个Page对象,并且把对象放到ThreadLocal中。AbstractHelperDialect中,Page对象中的翻页信息是通过getLocalPage()取出的:调的就是PageHelper的getLocalPage,从ThreadLocal中获取到。每次查询(每个线程)都有线程私有Page对象,里面有页码和每页数量。
应用场景分析:与Spring 整合分析
关键配置:pom依赖,出了mybatis依赖,还需要mys和spring整合包。叫mybatis-spring。版本要对应。SqlSessionFactoryBean在applicationContext.xml配置这个类。这个Bean会初始化SqlSessionFactory,用来创建SqlSession。属性要指定mybatis-config.xml和Mapper映射器文件。SqlSessionFactoryBean在哪创建?SqlSession在哪创建?代理类在哪创建?
创建会话工厂SqlSessionFactory:实现了三个接口:InitializingBean、FactoryBean、ApplicationListener。InitializingBean实现AfterPropertiesSet方法,在bean属性值设置完后调用。调用了buildSqlSesssionFactory方法。创建Configuration对象。创建解析全局配置文件XMLConfigBuilder。FactoryBean接口让用户自定义实例化Bean逻辑。获取SqlSessionFactoryBean,就会调用它的getObject方法。getObject方法调用了afterPropertiesSet方法,做mybatis解析配置,返回DefaultSqlSessionFactory。ApplicationListener监听ContextRefreshedEven(上下文刷新实践),会在SPring容器加载完后执行。
创建会话SqlSession:DefaultSqlSession是线程不安全的。mybatis-spring包,提供了线程安全的SqlSession包装类,SqlSessionTemplate。可以在所有DAO层共享实例(默认单例)。SqlSessionTemplate,增删改查都是调用代理对象的方法。代理对象在构造方法通过JDK动态代理创建:怎么拿到一个SqlSessionTemplate?提供抽象支持类SqlSessionDaoSupport,持有一个SqlSessionTemplate对象,提供getSqlSession方法。在实现类得方法里,可以直接调用父类封装的selectOne方法,最终会调用sqlSessionTemplate的selectOne方法。
接口的扫描注册:MapperScannerConfigurer用来扫描Mapper接口的。MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口。BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor子类,里面有一个postProcessBeanDefintionRegistry方法。MapperScannerConfigurer重写了postProcessBeanDefintionRegistry方法。创建了scanner对象,设置属性。
接口注入使用:Spring如何把 mybatis集成进去?提供SqlSession替代品SqlSessionTemplate,里面有一个实现InvocationHandler的内部SqlSessionInterceptor,本质是对SqlSession的代理。提供获取SqlSessionTemplate的抽象类SqlSessionDaoSupport。扫描Mapper接口,注册到容器中的是MapperFactoryBean。把Mapper注入使用的时候,调用的是getObject方法。执行Mapper接口任意方法,会走到触发管理类MapperProxy,进去SQL处理流程。学到了?1.为组件预留扩展接口。2.利用Spring扩展机制,把组件集成到mybatis中。设计模式总结:
参考资料:咕泡学院·MyBatis插件原理与Spring集成·青山