• mybatis源码学习(二)--mybatis+spring源码学习


    这篇笔记主要来就,mybatis是如何利用spring的扩展点来实现和spring的整合

    1.mybatis和spring整合之后,我们就不需要使用sqlSession.selectOne()这种方式了,可以直接从spring容器中获取到接口的代理对象,然后调用对应的目标方法,那么,mybatis在将接口交给spring管理的时候,用到了三个扩展点:

      1.1 factoryBean  mapperFactoryBean就是实现了factoryBean,然后,通过getObject方法来返回一个代理对象

      1.2 mapperFactoryBean同时继承了SqlSessionDao,SqlSessionDao继承了DaoSupport,DaoSuppor有实现了InitializingBean,在初始化mybatis接口的时候,会调用DaoSupport的afterPropertiesSet()方法,也就是spring的初始化方法

      1.3 mapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,在registerBeanDefinitions方法中,会对mapperScan注解声明的包,进行扫描,class,扫描到beanDefinitionMap中,然后完成bean的初始化

    2.mybatis在和spring整合的时候,需要用到一个注解 @MapperScan,这个注解使用了@Import,引入了mapperScannerRegistrar,关于import注解的作用,在spring源码解析(一)中有介绍,不详细说了,在将类put到beanDefinitionMap中的时候,会执行mapperScannerRegistrar的registryBeanDefintions方法,这时候,会调用doScan方法,将mapperScan对应包下的class扫描出来,放到beanDefinitionMap中,这里要提的一个点是:

     @ComponentScan和@MapperScan,两个注解的包可能会一样,扫描出来的所有.class文件都是一样的,但是注入到beanDefinitionMap中的时候,ComponentScan注解注入的是@Component、@Controller、@Repository、@Service注解对应的class,mapperScan注入的是接口对应的类,关于这个点,我debug看过源码,在后面会把代码贴出来

     1 public Set<BeanDefinitionHolder> doScan(String... basePackages) {
     2         Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
     3         if (beanDefinitions.isEmpty()) {
     4             this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
     5         } else {
     6             this.processBeanDefinitions(beanDefinitions);
     7         }
     8 
     9         return beanDefinitions;
    10     }

    这个方法就是mapperScannerRegistrar中要调用的扫描方法,supper.doScan()会把包下所有的接口注入到beanDefinitionMap中,在注入完之后,会对mybatis的beanDefinition进行一些处理,在第六行的这个方法中,

     1.把当前beanDefinition的beanClass设置为mapperFactoryBean.calss,这样设置,是为了spring在对bean进行初始化的时候,会执行bean的初始化方法(就是上面说到的afterPropertiesSet())方法,在执行mybatis接口的初始化方法的时候,会根据beanClass,来调用mapperFactoryBean的getObject()方法来返回一个代理对象;

     2.把beanDefinition的注入模型(autowiredMode)设置为2(byType);这里会设计到spring的注入模型,简单而言,如果是byType,那么,就不需要在bean中添加@Autowired和@Resource,只需要提供set方法即可(注意:这里的byType和@Resource不一样)


    由于mapperFatoryBean继承了daoSupport,daoSupport又实现了InitializingBean,所以,spring容器在对mybatis的接口进行初始化之后,进行属性注入,然后会进行初始化,调用DaoSuppro的afterPropertySet方法,其中,会调用子类的checkDaoConfig()方法,在checkDaoConfig中,调用了configuration.addMapper(this.mapperInterface);这个方法就是mybatis中,将class放到knownMappers这个map的操作

     1 protected void checkDaoConfig() {
     2         super.checkDaoConfig();
     3         Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
     4         Configuration configuration = this.getSqlSession().getConfiguration();
     5         if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
     6             try {
     7                 configuration.addMapper(this.mapperInterface);
     8             } catch (Exception var6) {
     9                 this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
    10                 throw new IllegalArgumentException(var6);
    11             } finally {
    12                 ErrorContext.instance().reset();
    13             }
    14         }
    15 
    16     }

    那mapperFactoryBean的getObject()方法是在什么时候执行呢?假如说我们在service中注入了mybatis的dao接口,在实例化service的时候,会进行属性注入,属性注入的时候,会发现,service中需要注入dao,这时候,会调用getBean()从spring容器中获取,如果dao,没有实例化,就会去实例化,在实例化完成之后,放到单实例池中,然后,会调用getObject()

    在调用getObject的时候,会通过jdk动态代理来生成一个代理对象

     1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
     2         MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
     3         if (mapperProxyFactory == null) {
     4             throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
     5         } else {
     6             try {
     7                 return mapperProxyFactory.newInstance(sqlSession);
     8             } catch (Exception var5) {
     9                 throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
    10             }
    11         }
    12     }

    这里生成代理对象和原生mybatis生成代理对象有一个区别,就是这里的sqlSession,原生的,用的是DefaultSqlSession,mybatis和spring整合之后,这里用的是sqlSessionTemplate,那为什么会是sqlSessionTemplate呢?

    在mapperFactoryBean的父类,SqlSessionDaoSupport中,会对sqlSession赋值,这时候,是直接new SqlSessionTemplate(SqlSessionFactory),所以,在和spring整合之后,mybatis用的sqlSession是sqlSessionTemplate

    在实际调用目标方法的时候,会被mapperProxy的invoke方法拦截,和原生mybatis不同的时候,在判断是select还是update,之后,原生的mybatis会调用sqlSession.selectOne();和spring整合之后,会调用SqlSessionTemplate.selectOne(),然后再调用sqlSessionInterceptor.invoke()方法

  • 相关阅读:
    nginx相关参考博客
    MySQL workbench8.0 CE基本用法(创建数据库、创建表、创建用户、设置用户权限、创建SQL语句脚本)
    MySQL Workbench基本操作
    idea导入(import)项目和打开(open)项目的区别
    [铁道部信息化管理]需求分析(一)—— 售票系统领域知识(区间票、订票、预留票)
    [铁道部信息化管理]核心业务需求及逻辑架构分析
    【spring boot 系列】spring data jpa 全面解析(实践 + 源码分析)
    OOAD-设计模式(一)概述
    TKmybatis的框架介绍和原理分析及Mybatis新特性
    国内程序员的十大疑问之一:为什么老外不愿意用MyBatis?
  • 原文地址:https://www.cnblogs.com/mpyn/p/11962680.html
Copyright © 2020-2023  润新知