Mybatis源码(二)mapper接口动态代理过程

本文分析下mapper接口被动态代理的过程。本系列文章,都是基于springboot的,因为在实际的生成环境中,大多都是这么用的。

上篇文章《Mybatis源码(一)利用springboot集成&Mapper接口加载过程》中,我们最后知道,mapper接口在注册bean定义的过程中,在AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions中,修改了bean定义,mapper接口最终都被注册为了MapperFactoryBean类型。也就是说所有mapper接口都成为了MapperFactoryBean,但是他们的对象是不相等的。这个bean内部有个 private Class mapperInterface;属性,每个对象虽然类型一样,但是这个属性值是不一样的。

看下MapperFactoryBean的类图:
在这里插入图片描述

1. Mapper接口实例化MapperFactoryBean

从spring的生命周期我们知道,bean注册后,在使用的时候就会被实例化,mapper接口也是如此。动态代理,就是发生在这个实例化的过程中。

spring实例化bean的过程,参考文章《spring5源码阅读(三)BeanFactory#getBean(String name)》的最后一节。

并且,动态代理是发生在mybatis的代码中,而非spring中;因为mybatis脱离spring也是可以单独使用的。mybatis的动态代理,也是基于jdk的动态代理实现。

现在加如我们在一个controller中引用了一个UsersMapper接口:

@Resource
private UsersMapper usersMapper;

那么当spring实例化controller的时候没必然就会先实例化依赖的bean,也就是usersMapper。

1. spring实例化代码位置:
AbstractBeanFactory#doGetBean() ->>getObjectForBeanInstance()

2. MapperFactoryBean#getObject()
因为mapper接口bean都被注册为了MapperFactoryBean,所以实例化对象是最终调用MapperFactoryBean#getObject()方法:

@Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

看下getSqlSession()方法:

public SqlSession getSqlSession() {
    return this.sqlSession;
  }

3. SqlSessionTemplate类
上篇文章中,我们提到了两个属性 sqlSessionTemplate 和 sqlSessionFactory,其中SqlSessionTemplate就是sqlSession接口的实现类

那么这俩属性是如何被注入的呢?
上篇文章最后我们知道mapper在注册定义的时候,设置了属性注入类型是AbstractBeanDefinition.AUTOWIRE_BY_TYPE,这样spring就会扫描目标bean中的set方法,并对相关属性进行属性注入。

通过类图发现MapperFactoryBean继承了类SqlSessionDaoSupport,看下类结构:
在这里插入图片描述
正好有两个set方法,这样spring就能注入sqlSessionTemplate 和 sqlSessionFactory这俩属性。这样spring在实例化mapper bean后,再填充属性的时候,就能把这俩属性注入进去。

继续上文getSqlSession().getMapper(this.mapperInterface),getSqlSession()返回的就是sqlSessionTemplate,所以getMapper() 就在SqlSessionTemplate中:

@Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }

SqlSessionFactory类
还得在看下getConfiguration()方法:

@Override
  public Configuration getConfiguration() {
    return this.sqlSessionFactory.getConfiguration();
  }

看,用的是sqlSessionFactory类,上面说的两个属性都用上了,至于这俩属性是如何被实例化的,放在后续文章中分析。

Configuration类
继续看getMapper()方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
  • Configuration:mybatis核心类,类似spring的beanfactory容器,存放了mybaits的属性配置,以及mapper的注册信息。Configuration的初始化是在SqlSessionFactory初始化的时候,比较复杂。mapper的xml文件,也是在这个过程中被扫描到。
  • mapperRegistry:Configuration中的一个属性,内部存放了mapper接口和MapperProxyFactory的映射关系。
    knownMappers.put(type, new MapperProxyFactory(type));

MapperProxyFactory就是用来创建代理类的。

2. MapperProxyFactory创建代理类

继续上文的mapperRegistry.getMapper(type, sqlSession);方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

继续进入newInstance方法:

 public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

到这基本就清楚了,就是利用的jdk的动态代理类Proxy实现。

代理类是MapperProxy,打开看看比较核心的invoke方法:

 @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //调用mapper接口方法时,最终就是这里执行的sql
    return mapperMethod.execute(sqlSession, args);
  }
发布了62 篇原创文章 · 获赞 29 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/csdn_20150804/article/details/102692590