动态代理的楷模:源码分析Mybatis与Spring(一)

Mybatis对于我们并不陌生,但他实际工作原理是怎样的呢?希望看完本篇文章,大家能了解一下问题

  • 调用的Mapper是接口,实际执行的实现类是什么?

源码分析

Mybatis有两处都用了动态代理。一是Mapper接口代理,二是SqlSession代理。具体实现,下面逐一剖析

一. Mapper接口代理

  • 1、Mapper Bean 的IOC

经历过 MapperScannerConfigurer 的初始化,包路径下的Mapper接口都注册成为的Spring的bean,其BeanName是接口名,BeanClass是 MapperFactoryBeanMapperFactoryBean 是工厂Bean,实现了 FactoryBean 这个接口。

public interface FactoryBean<T> {
    //返回对象的实例
    T getObject() throws Exception;
    //返回对象实例的类型
    Class<?> getObjectType();
    //是否为单例
    boolean isSingleton();
}
复制代码

再看看Spring在获取Bean时的调用链 getBean() -> doGetBean() -> AbstractBeanFactory::getObjectForBeanInstance() -> FactoryBeanRegistrySupport::getObjectFromFactoryBean() -> FactoryBean::getObject()

因此,当IOC调用时,返回的实例不是MapperFactoryBean对象本身,而是getObject()返回的实例,类型是getObjectType()返回的类型。

  • 2、getObject()

接下来看看MapperFactoryBean的 getObject() 实现了什么。

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

MapperFactoryBean继承了SqlSessionDaoSupport, getSqlSession() 返回的是 SqlSessionTemplate 。SqlSessionTemplate继承了 SqlSeesion 接口

  • 3、getMapper()

public class SqlSessionTemplate implements SqlSeesion
  @Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }
复制代码

getConfiguration()最终返回的是DefaultSqlSessionFactory的Configuration(Mybatis属性的大管家)

public Configuration{
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
}
复制代码

这里出现了MapperRegistry类,下面具体看看它的实现:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    ...
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
复制代码
  • 4、newInstance()

到了这里,终于看到了熟悉的动态代理身影。代理的是Mapper接口,处理类是MapperProxy。

public class MapperProxyFactory<T> {
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    // MapperProxy是
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}
复制代码
  • 5、invoke()

IOC调用Mapper的方法,最终会由MapperProxy使用当前线程的sqlSession执行JDBC操作。

@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 (method.isDefault()) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
复制代码
  • 6、addMapper()

在getMapper()时细心的小伙伴会存在疑问, final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); 是怎么来的。这要从 MapperRegistry 类的 addMapper() 方法说起

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        // 将注解中的sql转换成sqlStatement
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
复制代码

addMapper()会将当前接口类作为key,MapperProxyFactory为value存到knownMappers中。MapperProxyFactory会在getMapper()执行时取出,生成代理类。

那addMapper()一定是在getMapper()前调用的,实际调用时机是什么时候呢? 追根溯源, addMapper() <- Configuration::addMapper() <- MapperFactoryBean::checkDaoConfig() MapperFactoryBean的继承类如下:

再看看DaoSUpport类:

@Override
	public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
		// Let abstract subclasses check their configuration.
		checkDaoConfig();
    ...
	}
复制代码

结论:SpringBoot启动时会扫描包路径下所有Mapper接口注册成Bean,每个Mapper接口Bean的初始化完成并属性设置完成后,都会调用 checkDaoConfig() ,由此将Mapper加载到knownMappers供IOC时使用。当Spring GetBean时,最终会调用到getMapper(),返回当前Mapper接口的动态代理类作为实现类。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章