mybatis -spring 集成映射原理 --分析

mybatis -spring 集成映射原理 --分析:

基于的版本为:

4.3.9.RELEASE

3.4.4

1.3.1

实现原理分析

     对于以上极其简单代码看上去并无特殊之处,主要亮点在于 UserMapper 居然不用实现类,而且在调用 getUser 的时候,也是使用直接调用了UserMapper实现类,那么Mybatis是如何去实现 UserMapper的接口的呢?

    可能你马上能想到的实现机制就是通过动态代理方式,好吧,看看MyBatis整个的代理过程吧。

    首先在Spring的配置文件中看到下面的Bean:

   以上的MapperScannerConfigurer class的注释中描述道:

   从base 包中搜索所有下面所有 interface,并将其注册到 Spring Bean容器中,其注册的class bean是MapperFactoryBean。

   好吧,看看它的注册过程,下面方法来从 MapperScannerConfigurer中的Scanner类中抽取,下面方法在初始化以上application-content.xml文件时就会进行调用。主要用于是搜索 base packages 下的所有mapper class,并将其注册至 spring 的 benfinitionHolder中。

 

首先看到doScan:

/**

* Calls the parent search that will search and register all the candidates.

* Then the registered objects are post processed to set them as

* MapperFactoryBeans

*/

@Override

public Set doScan(String... basePackages) {

  Set 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;

}

 

private void processBeanDefinitions(Set beanDefinitions) {

  GenericBeanDefinition definition;

  for (BeanDefinitionHolder holder : beanDefinitions) {

    definition = (GenericBeanDefinition) holder.getBeanDefinition();

 

    if (logger.isDebugEnabled()) {

      logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()

        + "' and '" + definition.getBeanClassName() + "' mapperInterface");

    }

 

    // the mapper interface is the original class of the bean

    // but, the actual class of the bean is MapperFactoryBean

    definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59

    definition.setBeanClass(this.mapperFactoryBean.getClass());

 

    definition.getPropertyValues().add("addToConfig", this.addToConfig);

 

    boolean explicitFactoryUsed = false;

    if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {

      definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));

      explicitFactoryUsed = true;

    } else if (this.sqlSessionFactory != null) {

      definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);

      explicitFactoryUsed = true;

    }

 

    if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {

      if (explicitFactoryUsed) {

        logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");

      }

      definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));

      explicitFactoryUsed = true;

    } else if (this.sqlSessionTemplate != null) {

      if (explicitFactoryUsed) {

        logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");

      }

      definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);

      explicitFactoryUsed = true;

    }

 

    if (!explicitFactoryUsed) {

      if (logger.isDebugEnabled()) {

        logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");

      }

      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

    }

  }

}

#1: 了解Spring初始化Bean过程的人可能都知道,Spring 首先会将需要初始化的所有class先通过BeanDefinitionRegistry进行注册,并且将该Bean的一些属性信息(如scope、className、beanName等)保存至BeanDefinitionHolder中;Mybatis这里首先会调用Spring中的ClassPathBeanDefinitionScanner.doScan方法,将所有Mapper接口的class注册至BeanDefinitionHolder实例中,然后返回一个Set,其它包含了所有搜索到的Mapper class BeanDefinitionHolder对象。

#2: 首先,由于 #1:  中注册的都是接口class, 可以肯定的是接口是不能直接初始化的;实际 #2: 中for循环中替换当前所有 holder的 className为 MapperFactoryBean.class,并且将 mapper interface的class name setter 至 MapperFactoryBean 属性为 mapperInterface 中,也就是 #3: 代码所看到的。

  再看看 MapperFactoryBean,它是直接实现了 Spring 的 FactoryBean及InitializingBean 接口。其实既使不看这两个接口,当看MapperFactoryBean的classname就知道它是一个专门用于创建 Mapper 实例Bean的工厂。

  至此,已经完成了Spring与mybatis的整合的初始化配置的过程。

   接着,当我在以上的 Test类中,需要注入 UserMapper接口实例时,由于mybatis给所有的Mapper 实例注册都是一个MapperFactory的工厂,所以产生UserMapper实现仍需要 MapperFactoryBean来进行创建。接下来看看 MapperFactoryBean的处理过程。

   先需要创建Mapper实例时,首先在 MapperFactoryBean中执行的方法是:这里会将所有的接口加入到configuration中去,最终会放到MapperRegistery.knownMappers:具体的定义为:

private final Map, MapperProxyFactory> knownMappers = new HashMap, MapperProxyFactory>();

/**

* {@inheritDoc}

*/

@Override

protected void checkDaoConfig() {

  super.checkDaoConfig();

  notNull(this.mapperInterface, "Property 'mapperInterface' is required");

  Configuration configuration = getSqlSession().getConfiguration();

  if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {

    try {

      configuration.addMapper(this.mapperInterface);

    } catch (Exception e) {

      logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);

      throw new IllegalArgumentException(e);

    } finally {

      ErrorContext.instance().reset();

    }

  }

}

 

    上面方法中先会检测当前需要创建的 mapperInterface在全局的 Configuration中是否存在,如果不存在则添加。呆会再说他为什么要将 mapperInterface 添加至 Configuration中。该方法调用完成之后,就开始调用 getObject方法来产生mapper实例,看到MapperFactoryBean.getObject的该方法代码如下:

在MapperFactoryBean中获取具体的实体对象的,返回还是对应的interface,并不是真正的代理对象,继续下一步

/**

* {@inheritDoc}

*/

@Override

public T getObject() throws Exception {

  return getSqlSession().getMapper(this.mapperInterface);

}

从spring的SqlSessionTemplate获取

/**

* {@inheritDoc}

*/

@Override

public T getMapper(Class type) {

  return getConfiguration().getMapper(type, this);

}

于是,再进入 org.apache.ibatis.session.Configuration.getMapper中代码如下:

public T getMapper(Class type, SqlSession sqlSession) {

  return mapperRegistry.getMapper(type, sqlSession);

}

其中 mapperRegistry保存着当前所有的 mapperInterface class. 那么它在什么时候将 mapperinterface class 保存进入的呢?其实就是在上面的 afterPropertiesSet 中通过 configuration.addMapper(this.mapperInterface) 添加进入的。

@SuppressWarnings("unchecked")

public T getMapper(Class type, SqlSession sqlSession) {

  final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);

  }

}

 这里可以看到对应的根据对应的类型获取对应的MapperProxyFactory,然后newInstance返回对应的MapperProxy

public class MapperProxyFactory {

 

  private final Class mapperInterface;

  private final Map methodCache = new ConcurrentHashMap();

 

  public MapperProxyFactory(Class mapperInterface) {

    this.mapperInterface = mapperInterface;

  }

 

  public Class getMapperInterface() {

    return mapperInterface;

  }

 

  public Map getMethodCache() {

    return methodCache;

  }

 

  @SuppressWarnings("unchecked")

  protected T newInstance(MapperProxy mapperProxy) {

    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);

  }

 

  public T newInstance(SqlSession sqlSession) {

    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);

    return newInstance(mapperProxy);

  }

}

 JDK的动态代理就不说了,至此,基本Mybatis已经完成了Mapper实例的整个创建过程,也就是你在具体使用 UserMapper.getUser 时,它实际上调用的是 MapperProxy,因为此时 所返回的 MapperProxy是实现了 UserMapper接口的

 

 

你可能感兴趣的:(编程技术)