Spring与Mybatis结合的MapperScannerConfigurer处置过程源码剖析转载

原创
小哥 2年前 (2022-12-30) 阅读数 71 #大杂烩

本文将分析mybatis与spring整合的MapperScannerConfigurer的基本原理之前已经过分析。java要实现动态,可以使用jdk自带api和cglib第三方库生成动态代理。本文分析了mybatis版本3.2.7,mybatis-spring版本1.2.2。

MapperScannerConfigurer介绍

MapperScannerConfigurer 是spring和mybatis整合的mybatis-spring jar包中提供的类。

要了解这个类的作用,您必须首先了解 MapperFactoryBean

MapperFactoryBean为了取代手动使用SqlSessionDaoSupport或SqlSessionTemplate写入数据访问对象(DAO)的代码,使用动态代理实现。

例如,本官方文档中的以下配置:


  
  

org.mybatis.spring.sample.mapper.UserMapper是一个接口,我们创建一个MapperFactoryBean实例,然后注入此接口和。sqlSessionFactory(mybatis中提供的SqlSessionFactory接口,MapperFactoryBean会使用SqlSessionFactory创建SqlSession)这两个属性。

之后要使用此UserMapper接口,直接通过spring注入这个bean,然后您可以直接使用它,spring此接口的动态代理是在内部创建的。

当您发现要使用多个MapperFactoryBean同时,一个定义肯定很麻烦,所以mybatis-spring提供了MapperScannerConfigurer这个类将在类路径下查找映射器并自动创建它们MapperFactoryBean。


  

此配置扫描org.mybatis.spring.sample.mapper然后为每个接口创建一个动态代理类。

MapperScannerConfigurer基础代码分析

以下面的代码为例进行解释(省略了部分代码、其他代码和配置):

package org.format.dynamicproxy.mybatis.dao;
public interface UserDao {
    public User getById(int id);
    public int add(User user);    
    public int update(User user);    
    public int delete(User user);    
    public List getAll();    
}


    
    

让我们先看看测试用例debug查看userDao实现类到底是什么。

我们可以看到,userDao是1个MapperProxy类的实例。
看下MapperProxy源代码,是的,已实现InvocationHandler,表示使用jdk自包含的动态代理。

public class MapperProxy implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class mapperInterface;
  private final Map methodCache;

  public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

开始下面的分析。MapperScannerConfigurer的源码

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口是可以修改的接口spring在领班中定义bean接口,具有postProcessBeanDefinitionRegistry方法。

然后我们看ClassPathMapperScanner关键是如何扫描相应的package在界面下。

其实MapperScannerConfigurer的功能是转换相应接口的类型MapperFactoryBean,而这个MapperFactoryBean的属性mapperInterface是原始类型。MapperFactoryBean本文一开始就进行了分析。

所以最终我们要分析MapperFactoryBean实现原则!

MapperFactoryBean继承了SqlSessionDaoSupport类,SqlSessionDaoSupport类继承DaoSupport抽象类,DaoSupport抽象类实现InitializingBean接口,因此实例MapperFactoryBean何时,将致电InitializingBean接口的afterPropertiesSet方法。

DaoSupport的afterPropertiesSet方法:

MapperFactoryBean重写了checkDaoConfig方法:

然后通过spring工厂采取相应的bean的时候:

这里的SqlSession是SqlSessionTemplate,SqlSessionTemplate的getMapper方法:

Configuration的getMapper方法,将使用MapperRegistry的getMapper方法:

MapperRegistry的getMapper方法:

MapperProxyFactory构造MapperProxy:

没错! MapperProxyFactory要使用的jdk组带的Proxy完成动态代理。
MapperProxy一开始就提到了。MapperProxy内部使用。MapperMethod该类完成方法的调用:

下面,我们将UserDao的getById方法来debug看看MapperMethod的execute方法如何进行。

@Test
public void testGet() {
    int id = 1;
    System.out.println(userDao.getById(id));
}


示例代码: https://github.com/fangjian0423/dynamic-proxy-mybatis-study

总结

来到新公司,联系Mybatis,之前联系~ 但接触不深,突然发现spring与mybatis集成后,您只能编写接口而不实现它,spring默认值将帮助我们实现它,然后感觉非常神奇,所以我写了一篇文章。 java动态代码分析 和本文。

参考资料

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门