从源码看世界:Springboot整合Mybatis后到底做了什么
要在Springboot整合Mybatis,首先修改pom依赖:
<!--<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自动注入即可使用:
@Autowiredprivate StudentMapper studentMapper;
@GetMapping("/test")
public String test() {
Student student = studentMapper.getById(1);
return student.getName();
}
至此,Springboot已经成功整合Mybatis,使用起来的确十分方便。但Mybatis的步骤肯定没有减少,只不过spring帮我们做了而已,那它到底在哪里自动完成的呢?
从引入的依赖命名来看,这个是mybatis开箱即用的starter(具体原理请查阅springboot的starter原理,此处不再赘述),因此首先查看spring.factory指定了哪些自动配置类:
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。
既然SqlSessionFactory和SqlSession都有了,那两者是如何关联起来的呢?
还记得上面我们用了@MapperScan来指定mapper所在的包路径吧,因此可以在这个注解作为入口,里面最重要的是@Import(MapperScannerRegistrar.class),而MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar,得到BeanDefinitionRegistry对象,然后根据@MapperScan的配置注册了beanClass=MapperScannerConfigurer的BeanDefinition。
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方法:
@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会做判断,条件不足时会抛出异常,因此实例化自然就失败了
以上是 从源码看世界:Springboot整合Mybatis后到底做了什么 的全部内容, 来源链接: utcz.com/z/513064.html