Analysis of springboot mybatis mapper source code

        MybatisAutoConfiguration is the automatic configuration class of mybatis under springboot. This configuration class injects SqlSessionFactory and SqlSessionTemplate into the spring container. The injected SqlSessionFactory is obtained through getObject() of SqlSessionFactoryBean. When SqlSessionFactoryBean creates SqlSessionFactory through buildSqlSessionFactory(), the mapper will be added through MapperRegistry. The source code is as follows: 

 

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<T>(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);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

It can be seen that when the mapper is added, the mapper will be packaged into the MapperProxyFactory class. MapperRegistry also provides getMapper

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);
  }
}

When obtaining the mapper, the MapperProxyFactory wrapper class is obtained first, and then the final mapper object is obtained through the newInstance of the MapperProxyFactory.

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

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}

As can be seen from the above source code, the final return is an object generated by jdk dynamic proxy, as shown in the figure (this is the proxy object generated by mybatisplus), so all methods in the mapper interface are ultimately executed through the invoke method of MapperProxy. The mapper we inject into other spring-managed beans is this proxy object.

So how is getMapper called?

When spring creates a mapper object, the bean created by the createbean method is a MapperFactoryBean object. Spring obtains the final required bean object by calling getObject() of MapperFactoryBean, and then it will call the getMapper method of MapperRegistry.

So why are all the beans obtained by the mapper a MapperFactoryBean object? This starts with the bean definition. Let's briefly talk about it through @MapperScan.

@Import(MapperScannerRegistrar.class)
public @interface MapperScan 

MapperScan defines the path to scan the mapper interface. MapperScannerRegistrar implements the ImportBeanDefinitionRegistrar interface, which is an extended interface for bean definitions by spring. Spring data also processes the definition and generation of the repository by implementing this interface.

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry);
    }
  }

  void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

 

        Mybatis scans the package path defined by MapperScan through ClassPathMapperScanner. ClassPathMapperScanner inherits ClassPathBeanDefinitionScanner. ClassPathMapperScanner first scans through ClassPathBeanDefinitionScanner (scanning in the specified basic package and returns the registered bean definition), and then modifies the beanClass defined by the bean through the processBeanDefinitions method. MapperFactoryBean, Spring generates a MapperFactoryBean object when it generates a bean object through bean definition. The source code is as follows: 

 

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  GenericBeanDefinition definition;
  for (BeanDefinitionHolder holder : beanDefinitions) {
    ...
    definition.setBeanClass(this.mapperFactoryBean.getClass());
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  Set<BeanDefinitionHolder> 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;
}

 

 In some versions, the registerBeanDefinitions method of MapperScannerRegistrar is not directly scanned by ClassPathMapperScanner, but the MapperScannerConfigurer is registered in spring. MapperScannerConfigurer implements the BeanDefinitionRegistryPostProcessor interface. BeanDefinitionRegistryPostProcessor inherits the BeanFactoryPostDefinitionProcessor. Spring will call the MapperScannerConfigurist's postProcess method at startup. After scanning the mapper, the processBeanDefinitions method of MapperScannerRegistrar is finally called to change the bean definition of the mapper.

Guess you like

Origin blog.csdn.net/sinat_33472737/article/details/106423656