从源码看世界: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自动注入即可使用:

@Autowired

private 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

回到顶部