先看一个简单的例子,以Service调用Mapper接口为例:java
public interface StudentMapper { @Select("select * from student") public List<Map<String,Object>> query();}
@Service("studentService")public class StudentServiceImpl implements StudentService { @Autowired StudentMapper studentMapper;
@Override public List<Map<String, Object>> query() { return studentMapper.select(); }}
向Service中注入这个Mapper并调用时,你知道这时注入的是什么吗?spring
经过调试,能够知道这时实际的studentMapper是一个类型为MapperProxy的代理对象,下面将从myabtis环境初始化开始,具体分析代理对象的产生过程。(不熟悉代理对象的同窗,能够查看以前专门讲java代理的文章)sql
1、配置SqlSessionFactoryBean 时都作了什么?mybatis
在进行spring和mybatis整合时,会用xml或者注解的方式去配置一个SqlSessionFactoryBean,本文中以注解方式为例:app
@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean;}
看一下SqlSessionFactoryBean的继承实现关系:框架
先看spring中两个很是重要的接口,FactoryBean和InitializingBean。ide
FactoryBean:ui
FactoryBean是一个spring中比较特殊的Bean,经过它的getObject()方法能够返回一个对象实例。SqlSessionFactoryBean中getObject()方法的实现:spa
在这里用于建立并返回一个SqlSessionFactory,在 spring +mybatis 的环境下,咱们使用SqlSessionFactoryBean来充当SqlSessionFactory。代理
InitializingBean:
InitializingBean接口中只有一个方法,afterPropertiesSet(),全部实现了该接口的类,在bean的初始化以前都要调用这个方法。能够看出在上面的getObject方法中,若是SqlSessionFactory为空,会调用这个方法建立SqlSessionFactory。
经过调用SqlSessionFactoryBuilder的build方法,最终返回了一个DefaultSqlSessionFactory实例,这个DefaultSqlSessionFactory中保存了一个很是重要的Configuration对象。
2、@MapperScan都作了什么?
在注解配置mybatis时,经过@MapperScan指定Mapper存放的包,就能自动为咱们把接口实现成类。那么这是怎么实现的呢?
点开@MapperScan的源码,发现上面还有一行很是重要的注解:
@Import(MapperScannerRegistrar.class)
ImportBeanDefinitionRegistrar接口提供registerBeanDefinitions方法向用户暴露了BeanDefinitionRegistry,也就是说可让用户手动建立BeanDefinition并使用该注册器注册到spring容器中。
查看MapperScannerRegistrar的方法registerBeanDefinitions中的核心代码:
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);……scanner.doScan(StringUtils.toStringArray(basePackages));
主要是建立了一个Mapper扫描器,开启扫描。
ClassPathMapperScanner中doScan方法:
这里对生成的mapper的bean定义作了进一步处理
进入processBeanDefinitions()方法:
注意画框代码及上方的注释,先看一下从BeanDefinitionHolder得到BeanDefinition时beanClass初始的值:
等待setBeanClass执行完毕:
经过definition.setBeanClass()把原来的BeanClass的类型替换成了MapperFactoryBean类型。到这,完成了Mapper接口加载定义阶段中很是重要的一步,而这也是生成代理对象MapperProxy的关键。
3、mybatis如何生成代理对象?
看一下MapperFactoryBean的继承关系:
MapperFactoryBean继承的SqlSessionDaoSupport类实现了InitializingBean接口,那么咱们仍是首先找afterPropertiesSet()方法:
DaoSupport中,最终调用MapperFactoryBean中的方法:
首先经过获取sqlSession得到了很是重要的配置类Configuration,而后查看一下addMapper方法,最终调用的是MapperRegistry的addMapper方法:
红框中的代码为咱们建立了Mapper 的代理工厂对象(还不是Mapper的代理对象),并把它放入了knownMappers这个Map中。
在这一步,只是简单初始化了MapperProxyFactory,把咱们本身的mapper的类型传给了它,还并无真正产生代理对象。
MapperRegistry并在以后的parse()方法中完成了xml文件的解析,每个sql方法都被解析成了一个MappedStatement对象,并添加到了配置类Configuration对象中。
MapperFactoryBean最终返回了什么?
由于MapperFactoryBean实现了FactoryBean接口,因此咱们看看getObject方法究竟返回了什么:
最终调用MapperRegistry的getMapper方法:
这里调用的了mybatis刚才生成的MapperProxyFactory,帮助咱们实例化并返回了一个代理对象。MapperProxyFactory中使用newInstance方法,实例化MapperProxy,用于生成代理:
至此,咱们已经弄明白了文章开头的MapperProxy是如何生成的。
4、MapperProxy代理对象如何执行sql语句?
在StudentServiceImpl中的query方法中打一个断点跟踪语句,你会发现实际执行的就是代理类MapperProxy中的invoke()方法。
MapperProxy在做为代理类的同时,自身实现了InvocationHandler接口,因此invoke方法就是真正执行的代理逻辑。
在这里最终调用了MapperMethod的execute方法实际去执行了sql语句。
在该方法中,根据sql语句执行类型,调用sqlSession对应的方法执行并将结果返回给用户。至此,mybatis在spring环境下一次调用所有完成。