先看一個簡單的例子,以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環境下一次調用所有完成。