现在主流的spring整合mybatis用注解@MapperScan("com.cat.mapper")
这个注解里包含了注解@Import(MapperScannerRegistrar.class)
在spring源码那篇讲到.扫描到注解@import的时候.会处理里面的class
这里的MapperScannerRegistrar implements ImportBeanDefinitionRegistrar ;所以 spring会去运行实现接口的重写方法;
图1:解析注解@mapperscan,封装到annotationattribute对象里.
图2:然后用classpathbeandefinitonsacn.scan(方法解析扫描的包
图3: 这个scan方法先调用父类去把包下面的所有class添加到bean工厂的beandefinitionmap中.并且返回所有的beandefinition(bd);接着调用processbean....()方法处理这些bd,跟踪该方法
图4:处理bd方法把所有的bd的class都设成了mapperfactorybean;
因此后期再创建bean对象的时候,其实是会创建中出mapperfactorybean的类型.而这个类实现了factorybean接口.所以真正的类型是实现该接口的getobject()方法的返回值:
接下来是重点的地方:
其中该类型继承了一个类,这个类点进去发现其实
SqlSessionDaoSupport extends DaoSupport implements InitializingBean
而其中 InitializingBean 的重写方法afterPropertiesSet这个方法是在bean初始化后执行;这个方法会执行子类的实现方法checkdaoconfig()-如上图;作用是把该类的包名+类名+方法名作为id.和对应的方法sql等属性分装到对象MappedStatement中.
然后把该对象放到configuration对象的一个map中key为包名+类名+方法名作为id,value 为MappedStatement;
这里的getsqlsession()是spring用了bytype自动装配初始化的对象叫sqlsessiontemplate.如果mybatis没交给spring管理的话创建的是defaultsqlsession;这两个对象的区别非常重要,spring创建失去了session对象使得mybatis的一级缓存失效.看源码可以知道每次的执行sql都会在finally里吧该sqlsession关闭了.(为什么spring要自己关闭,因为没提供向外暴露扩展进行手动关闭).而mybatis的defultsqlsession是不会关闭的.所以一级缓存是有效的;
下面的逻辑是调用mapper方法的时候的逻辑:
跟中代码到这个.发现.用到了jdk动态代理.生成代理对象.且mapperproxy这个类为具体方法实现,继续跟踪这个类的invok方法.
这里.不展开了.比较复杂.基本上是获取到对象mappermethod.该对象包含了该方法的执行sql对象以及返回类型.这信息都是从mappstament对象里拿到的.最后执行execute.里面判断语句方式,.返回类型等.
最后执行该语句.是把sqlsessiontemplate做了个代理对象.,然后执行该代理对象的invoke方法.该方法里就会把sqlsession关闭.从而使一级缓存失效;
(mybatis的二级缓存虽然是跨sqlsession对象的但是只能在同一个命名空间里.,也就是如果其他空间有对数据进行修改.那么这时候查出来的sql还是用该空间的缓存,会脏读.不建议使用,)
总结:
mybatis给spring管理的话,在bean初始化的时候会把sql的信息和方法关系存起来.然后生成了一个代理对象,
然后在调用方法的时候,如query(),该代理对象又会为其从新创建一个sqlsessionproxy代理对象,进行运行sql.且该代理对象最后关闭sqlsession.
efdfd