在Mybatis一次數據庫操做過程的文章中,我展現了使用Mybatis操做數據庫的demo,但實際使用時並不會這裏寫代碼,由於通常都會使用springboot了,那如今咱們一塊兒來看看Springboot整合Mybatis以後到底爲咱們作了哪些事情。java
要在Springboot整合Mybatis,首先修改pom依賴:spring
<!--<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency>--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency>
而後在具體的mapper類中增長@Repository,讓spring管理其實例;接着在Application增長@MapperScan("mapper所在的包路徑")。數據庫
如今,只要在須要使用mapper的地方使用@Autowired自動注入便可使用:springboot
@Autowired private StudentMapper studentMapper; @GetMapping("/test") public String test() { Student student = studentMapper.getById(1); return student.getName(); }
至此,Springboot已經成功整合Mybatis,使用起來的確十分方便。但Mybatis的步驟確定沒有減小,只不過spring幫咱們作了而已,那它到底在哪裏自動完成的呢?mybatis
從引入的依賴命名來看,這個是mybatis開箱即用的starter(具體原理請查閱springboot的starter原理,此處再也不贅述),所以首先查看spring.factory指定了哪些自動配置類:app
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
第一個是動態語言驅動,主要看MybatisAutoConfiguration:裏面註冊了兩個實例SqlSessionFactory與SqlSessionTemplate。注意,它們同時用了@ConditionalOnMissingBean註解,即沒有這個bean的時候才生效,例如咱們本身註冊了SqlSessionFactory,所以MybatisAutoConfiguration的就再也不實例化了。而SqlSessionTemplate是SqlSession的實現類,能夠推測spring使用這個代替mybatis的DefaultSqlSession。ide
既然SqlSessionFactory和SqlSession都有了,那二者是如何關聯起來的呢?spring-boot
還記得上面咱們用了@MapperScan來指定mapper所在的包路徑吧,所以能夠在這個註解做爲入口,裏面最重要的是@Import(MapperScannerRegistrar.class),而MapperScannerRegistrar實現了ImportBeanDefinitionRegistrar,獲得BeanDefinitionRegistry對象,而後根據@MapperScan的配置註冊了beanClass=MapperScannerConfigurer的BeanDefinition。ui
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); // ... registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); }
再來看看MapperScannerConfigurer,它實現了BeanDefinitionRegistryPostProcessor,也能獲得BeanDefinitionRegistry對象,而後建立了ClassPathMapperScanner對象,從命名上看能夠推測是掃描mapper用的,它繼承了ClassPathBeanDefinitionScanner,同時重寫了doScan方法:this
@Override public Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else { processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
一旦掃描到須要實例化的侯選對象,就會進入processBeanDefinitions方法。這裏細心的同窗可能會問,到底哪些侯選對象對符合條件呢?ClassPathMapperScanner還重寫了isCandidateComponent方法:
@Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); }
即既是接口又是獨立類才符合條件。
在processBeanDefinitions最重要的做用是將侯選對象的beanClass改成MapperFactoryBean,從命名也能夠推測是實例化Mapper的代理工廠,而它確實實現了FactoryBean,當mapper真正須要實例化的時候,就會調用MapperFactoryBean的getObject方法:
@Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }
而getSqlSession()得到的正是SqlSessionTemplate,至此SqlSessionFactory和SqlSession就關聯起來了。
最後,可能還有同窗會問:難道既是接口又是獨立類就做爲mapper來處理了嗎,不會代理錯了嗎?這個其實無需擔憂,由於MapperProxy會作判斷,條件不足時會拋出異常,所以實例化天然就失敗了