mybatis(三)插件原理和Spring集成

目标:
掌握插件的使用方法和工作原理
掌握自定义插件的编写方法
掌握Spring集成mybatis的原理

mybatis插件

使用PageHelper插件

在mybatis-config.xml中配置plugins标签
PageHelper.startPage(0,5);

插件的实现

	实现插件的3个步骤	
	
	1、实现Inteceptor接口
	2、重写intercept
	3、通过注解 指定要拦截的对象、方法、参数

可以参考下PageHelper的注解
在这里插入图片描述

	插件不用改动原来的查询功能,就得到了新的增强,肯定是用到了代理模式
	如果有多个插件层层拦截,用到了责任链模式、

哪些对象、哪些方法 可以被拦截

	当然是被代理的对象

在这里插入图片描述
Executor 如果有二级缓存,那么先创建基本类型、再用二级缓存装饰、最后插件拦截

插件的原理

思考下:

1、使用代理模式,是jdk还是cglib动态代理
2、代理类何时创建,解析配置?创建会话?还是执行sql时
3、核心的对象被调用,流程是怎样的。如何依次执行多个插件

对Executor的拦截是 openSession时创建的。

Executor executor = this.configuration.newExecutor(tx, execType);
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);

可以看到pluginAll 里迭代包装了我们原先创建的Executor。

Plugin 这个类是一个实现了InvocationHandler的类,所以他是代理管理类。

看看他的包装方法wrap(),

在wrap()方法中创建了一个Plugin对象,他封装了被代理类、Inteceptor
被代理对象有几个插件,就要被代理几次

被代理了肯定得先走代理管理类的invoke()方法

先走intercptor的intercept() ,方法为空直接走代理方法

在这里插入图片描述
如果有多个插件,再走一次Plugin的invoke()

配置的顺序和执行的顺序

配置的顺序跟执行的顺序是相反的,最先配置的插件离被代理类最近,必然最后执行

总结:

在这里插入图片描述

PageHelper原理

先看下PageHelper使用

		PageHelper.startPage(0,5);

        List<User> users = userMapper.queryUsers();

        PageInfo pageInfo = new PageInfo(users,10);
        

先设置分页start和limit,再把生成的结果封装成PageInfo 返回给前台

SQL改写的实现,PageHelper如何通过拦截器实现分页
先看下他的实现方法
在这里插入图片描述
这个getPageSql针对多种数据库,有不同的实现方式
在这里插入图片描述
mysql:
在这里插入图片描述
oracle
在这里插入图片描述

如何把start和limit参数传给插件的

看下PageHelper.startPage(); 找到一个setLocalPage(page);
发现这个是一个ThreadLocal 分页副本。

而在AbstractHelperDialect中的 getPageSql (),获取分页信息是从getLocal中获取的。

所以每查一次就会有一个线程私有的Page变量

关键类:

在这里插入图片描述

常用场景、自定义插件

在这里插入图片描述
当一个表数据太大时,进行按月份分表
fee_202001-fee_202012
表有三个字段 id,费用,日期
sql:查询按照日期查询
实现思路:根据日期查询时,自动把表替换为对应的月份表

Spring集成

原生的mybatis api主要有三个关键类

	SqlSessionFactory
	SqlSession
	getMapper返回的 mapper接口代理对象

虽然使用mybatis后,变的简单了,但是没有集成spring是没有灵魂的。

为了集成Spring,mybatis拓展了相关接口。把这三个对象都封装起来了
配置依赖

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>

配置数据源
配置mapper.xml路径
配置Mapper注解或者启动是MapperScan扫描包路径

就可以把mapper对象注入到Service中去使用了

思考下集成时发生了什么;

	1、通过注解注入了一个接口,这个接口在IOC中也是一个代理对象吗
	2、如果是代理对象,是不是还是用SqlSession执行的sql,SqlSession何时创建的
	3、单例的SqlSessionFactory何时创建

配置依赖

applicationContext.xml中配置
SqlSessionFactory和mapper.xml

了解spring和mybatis的集成,只需要了解

1、SqlSessionFactory在哪里创建
2、SqlSession在哪里创建
3、代理类在哪里创建

创建SqlSessionFactory

直接进SqlSessionFactoryBean 看,不出意外实现了FactoryBean

SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener

InitializingBean

实现了InitializingBean的afterPropertiesSet()方法
调用buildSqlSessionFactory()方法,这里创建一个Configuration targetConfiguration; 
存储所有的xml配置,熟悉的味道。
下面一堆typeAliases、plugins、typeHandlersPackage的解析,都是从这里来

在这里插入图片描述
这里通过xml解析的parse()方法和数据源enviroment都在源码分析里看到过,很类似
再下面就该到了解析Mapper环节了

xmlMapperBuilder.parse();喜闻乐见

mapper的parse()方法里做了两件事情

1、把增删改查标签换成statement标签
2、把mapper接口的class对象和对相应的Mapper代理工厂注册到MapperRegistry

最后调用一下build方法,还是返回一个DefaultSqlSessionFactory对象

sqlSessionFactoryBuilder.build(targetConfiguration);

总结:

通过实现InitializingBean ,重写afterPropertiesSet 方法,会在bean的属性设置完的时候被调用。
spring在启动这个bean的时候,实现了解析和创建工厂类
这个阶段的准备工作都是类似的,先解析配置,存到一个Configuration的对象中,把数据源交给事务管理,
解析mapper接口资源,每一个mapper接口都和他的代理工厂被注册起来
最后返回一个DefaultSessionFactory

FactoryBean

FactoryBean是spring的一个Bean接口,负责生产和装饰Bean,这里通过重写getObject(),里面调用
afterPropertiesSet(),去做mybatis的解析配置工作

ApplicationListener

实现这个接口是为了让DefaultSqlSessionFactory监控应用发出的一些事件
这里监听了 ContextRefreshedEvent ,上下文刷新事件,在spring容器加载之后执行
这里检查了ms是否加载完毕

在这里插入图片描述

创建会话SqlSession

在spring里没有直接用DefaultSqlSession,因为他是线程不安全的。之前我们每次会话会创建一个
Sqlsession,所以不会存在线程安全问题,现在交给容器管理,所以要考虑线程问题
Spring提供了SqlSessionTemplate ,他是SqlSession包装类,他也是单例的,可以被所有dao共享

打开SqlSessionTemplate ,发现里面也有很多curd方法,但是没有自己实现,都是通过一个代理调用的
this.sqlSessionProxy.selectOne(statement);
可以看到在初始化时,他是通过jdk动态代理实现的。。
那么所有的调用方法都会先执行 SqlSessionInterceptor 的invoke()方法

SqlSessionInterceptor 这个内部类invoke放

通过SqlSessionUtils 创建了一个SqlSession
把SqlSessionFactory、执行器类型、异常解析器都传进去
通过这个内部类的invoke方法可以看到,通过代理模式,调用一次方法,就走一次invoke,产生一个DefaultSqlSession的实例,保证了线程安全性。。

SqlSessionTemplate

SqlSessionTemplate 可以简化mybatis在spring中的使用

如何获取SqlSessionTemplate,并且在任何需要替代DefaultSqlSession的地方都可以使用,并且也必须是单例

mybatis提供了一个抽象类  SqlSessionDaoSupport,持有了SqlSessionTemplate对象
1、继承SqlSessionDaoSupport,再调用提供的增删改查
2、定义一个BaseDao继承SqlSessionDaoSupport,封装调用方法,Mapper接口实现类再继承BaseDao

上面的比较繁琐,还是使用Mapper接口代理类来实现方法的调用
Mapper接口的代理类肯定存在于BeanFactory中

思考:
1、何时注册到容器中的
2、注册的时候,是代理对象吗?那显然是一定

接口的扫描
在applicationContext.xml中配置了扫描包路径的MapperScannerConfigurer
打开 MapperScannerConfigurer,他实现了BeanDefinitionRegistryPostProcessor接口,主要是重写了postProcessBeanDefinitionRegistry() 这个方法
实现了这个方法就可以在spring创建Bean之前,修改某些Bean的内容,看下做了什么

1、把先扫描的包路径下的接口都封装成BeanDefinition,放到一个set里
2、processBeanDefinitions 处理 每一个BeanDefinition
definition.setBeanClass(this.mapperFactoryBeanClass);
这里BeanClass被注册成支持泛型的mapperFactoryBean,为什么要这样做
因为:MapperFactoryBean继承的SqlSessionSupport实现的FactoryBean;
继承SqlSessionSupport 意味着么一个注入Mapper的地方都可以拿到SqlSessionTemplate

接口注入使用
看看MapperFactoryBean类的getObject()可以看到调用的SqlSessionTemplate的getMapper方法
SqlSessionTemplate的本质就是一个代理,最终调用的getMapper()和DefaultSqlSession一样,都是
通过mapperProxyFactory 这个代理工厂创建jdk动态代理对象

总结:mybatis和spring集成时,spring做了什么

1、SqlSession的替代品SqlSessionTemplate,他有个内部类SqlSessionInterceptor,本质是对SqlSession的代理
2、提供了获取SqlSessionTemplate的抽象类SqlSessionDaoSupport
3、扫描Mapper接口,注册到容器中的是MapperFactoryBean,他继承了SqlSessionDaoSupport,可以获得
SqlSessionTemplate
4、mapper接口注入使用的时候,调用getObject方法,这里调用的是SqlSessionTemplate的getMapper
用jdk动态代理类返回的一个代理实例
5、调用Mapper接口的任意方法,触发管理类MapperProxy,进入sql流程

通过mybatis集成spring,思考下

1、为组件预留拓展接口
2、利用Spring的拓展机制,把组件集中到mybatis中

在这里插入图片描述
设计模式总结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值