Source code analysis Mapper objects Mybatis MapperProxy initialize the scan and Construction (illustrated)

@ [TOC] (Code Construction and analysis of the scanned object Mybatis MapperProxy Mapper initialization)

Tips: This article is based Mybatis.3.x version.

MapperScannerConfigurer, Spring integration Mybatis core classes, whose role is scanning project Dao class, to create it as an object that is Mybatis of Maper MapperProxy object.

Before entering the first source of learning, we look at the profile information in the project.

Here Insert Picture Description
We note here two or three related Mapper configuration:

  1. SqlSessionFactory # mapperLocations, you specify the path to the configuration xml file.
  2. SqlSessionFactory # configLocation, designated mybaits profile, the profile can also be configured to configure the path information mapper.xml.
  3. MapperScannerConfigurer, java class scanning Mapper (DAO).

Wording ideas are as follows:

  1. Scanning the object with the construction of Mybatis MapperProxy
  2. How Mapper class SQL statements associated with this part mainly elaborated running instances of Java classes Mapper objects (eg UserMapper, BookMapper) how (to establish contact UserMapper.xml, BookMapper.xml file) and mapper.xml.

Mybatis MapperProxy object creation process

The following might be more boring source code analysis, source code analysis before entering, to give first create a sequence diagram MapperProxy.

1.1 MapperProxy create a sequence diagram

Here Insert Picture Description

1.2 MapperScannerConfigurer Detailed

MapperScannerConfigurer class diagram is shown below:

Here Insert Picture Description
MapperScannerConfigurer achieve Spring Bean life cycle associated with the class: BeanNameAware, ApplicationContextAware, BeanFactoryPostProcessor, InitializingBean, BeanDefinitionRegistryPostProcessor, we first look at the time and method of calling these interfaces corresponding to:

  • BeanNameAware Bean is the name of their own perception, that is, when Bean created automatically arranged in the name of Bean Bean, the external application does not need to call setBeanName, you can get its bean name by getBeanName () method.
  • ApplicationContextAware auto-sensing ApplicationContext object, that is created when Bean, Spring plant will automatically injected into the current ApplicationContext Bean in.
  • InitializingBean implement the interface, Spring will automatically call InitializingBean # afterPropertiesSet method after initialization Bean.
  • BeanFactoryPostProcessor the BeanFactory post processor, but this time the created definition information of the Bean (the BeanDefinition), the method postProcessBeanFactory BeanFactoryPostProcessor interface, we can modify the bean definition information, such as modifying the value of an attribute, modifying the bean scope singleton or more cases. Is the BeanPostProcessor similar thereto, this is carried out on bean Bean before and after initialization, i.e., after the constructor call bean, init-method prior to execution.
  • BeanDefinitionRegistryPostProcessor mainly used to increase the definition of Bean, increasing BeanDefinition. Because MapperScannerConfigurer main purpose is to scan a particular package, and creates a corresponding Mapper objects, estimate the focus here is MapperScannerConfigurer implement.

That we are going to start tracking the interface from the implementation of BeanDefinitionRegistryPostProcessor.

BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);     // @1
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);   
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));    // @2
}
复制代码

@ 1 Code: SqlSessionFactory first set, generated from the Scan Mapper is ultimately governed by that of SqlSessionFactory. Code @ 2: scan method call ClassPathMapperScanner scan operation is performed, the following detailed description.

ClassPathMapperScanner#doScan

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);   //@1
    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      processBeanDefinitions(beanDefinitions);   // @2
    }
    return beanDefinitions;
}
复制代码

@ 1 Code: first calls the parent class (org.springframework.context.annotation.ClassPathBeanDefinitionScanner) method, according to the scanning of documents, corresponding BeanDefinitionHolder constructed object. Code @ 2: BeanDefinitions these processing, processing Bean added Mybatis characteristics.

ClassPathMapperScanner#processBeanDefinitions

private void processBeanDefinitions(Set<BeanDefinitionHolder> 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.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
      definition.setBeanClass(this.mapperFactoryBean.getClass());   // @1
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {    // @2 start
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }    // @2 end

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {  // @3
        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);
      }
    }
  }
复制代码

The method has three key points: @ 1 Code: beanClass class is provided in BeanDefinition The MapperFactoryBean, i.e., the instance is initialized BeanDefinition The MapperFactoryBean, its name implies, this is a FactoryBean objects, which will be constructed by the method getObject specific examples.

@ 2 Code: Set for MapperFactoryBean property, SqlSessionFactory into its properties may be automatically acquired when the SqlSessionFactory instantiated.

Code @ 3: sqlSessionTemplate If not empty, then put into the property, can be obtained so that the corresponding SqlSessionTemplate Spring at instantiation MapperFactoryBean.

Analysis here, doScan method MapperScannerConfigurer's over, but did not initialize Mapper, just create a lot of BeanDefinition, and its beanClass is MapperFactoryBean, then we will look to MapperFactoryBean.

1.3 Folders Factory Bean

MapperFactoryBean class diagram as follows:

Here Insert Picture Description
First make a snapshot of the above-mentioned core classes:

DaoSupport

Dao layer base class, defining a template method, for Subclasses implement specific logic, DaoSupport template method are as follows:

public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
	// Let abstract subclasses check their configuration.
	checkDaoConfig(); // @1

	// Let concrete implementations initialize themselves.
	try {
		initDao();           // @2
	} catch (Exception ex) {
		throw new BeanInitializationException("Initialization of DAO failed", ex);
	}
}
复制代码

Code @ 1: Check or construct dao configuration information, the method is an abstract class, subclass for realization, we wait for the next lead in this section is mainly MapperFactoryBean implementing the method, in order to achieve the integration of information related Mybatis. @ Code 2: The method of initialization related Dao, which is an empty implementation.

SqlSessionDaoSupport

SqlSession support parent class, create SqlSession by using SqlSessionFactory or SqlSessionTemplate, then the following two methods will be called when?

public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
复制代码

Do not know if you remember, when creating MapperFactoryBean of its property in sets SqlSessionFacotry or SqlSessionTemplate, supra Code (processBeanDefinitions), so in the example of Bean, Spring will automatically inject instance, that in the instance of the Bean when, in the above-described method, one or more will be called.

Folders Factory Bean

Mainly to see how it is implemented checkDaoConfig of.

MapperFactoryBean#checkDaoConfig
protected void checkDaoConfig() {
    super.checkDaoConfig();   // @1

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

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {     // @2
      try {
        configuration.addMapper(this.mapperInterface);                                        
      } catch (Throwable t) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
        throw new IllegalArgumentException(t);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }
复制代码

Code @ 1: First checkDaoConfig first call the parent class method. Code @ 2: mapperInterface, is a specific Mapper interface classes, such as com.demo.dao.UserMapper, if registered, an exception is thrown, or the call configuration to increase Mapper.

Next, enter the org.apache.ibatis.session.Configuration in.

public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
 }

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

public boolean hasMapper(Class<?> type) {
   return mapperRegistry.hasMapper(type);
}
复制代码

As can be seen from the above code is registered (added), query, access Mapper core classes for the MapperRegistry.

1.4 Folders Registry

The core class diagram is shown below:

Here Insert Picture Description
Its properties make a brief introduction:

  • Configuration config Mybatis global configuration objects.
  • Map <Class, MapperProxyFactory> knownMappers registered Map, the key here is mapper interface, such as com.demo.dao.UserMapper, is MapperProxyFactory, create MapperProxy factory.

The following outlines a few ways MapperRegistry, which are relatively simple to achieve.

Folders Registry # addMapper

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {   // @1
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));    // @2
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);    // @3
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
复制代码

Code @ 1: If the interface is already registered, an exception is thrown already bound. Code @ 2: Interface for registered MapperProxyFactory, but here just to register its creation MapperProxy factory, not create MapperProxy. Code @ 3: If resources Mapper corresponding xml not loaded, the xml binding operation is triggered, the xml sql statement with Mapper to build relationships. This article does not describe in detail, the next one in detail.

Note: addMapper method, but creates a corresponding MapperProxyFactory corresponding to * Mapper.

MapperRegistry#getMapper

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

MapperProxy create objects based Mapper interface SqlSession. @ 1 Code: Get MapperProxyFactory The interface. Code @ 2: Call MapperProxyFactory of newInstance create MapperProxy object.

Initialization construction process so far Mybatis Mapper's half done, that MapperScannerConfigurer by scanning package, and then build MapperProxy, but this time MapperProxy not yet associated with mapper.xml file sql statement, reasons of space, will be in the next section focuses on the process of establishing their relationship. Next, we get a preview of MapperProxy objects, after all, this is the ultimate target article to be created, but also to be a simple process to prepare for the implementation of the follow-up of SQL.

1.5 MapperProxy

FIG class as follows:

Here Insert Picture Description
The above type are relatively simple, MapperMethod, representative of a method Mapper can be seen from the SqlCommand, each corresponding to an SQL statement will MapperMethod.

Below is a diagram with SqlSessionFacotry each core class perspective:

Here Insert Picture Description

Tips: This article only describes the creation process of Mybatis MapperProxy, MapperProxy herein SQL * .Mapper.xml That is how the association is not related to the contents of this part see below, the upcoming release.

The authors introduce: "RocketMQ Technology Insider," the author, maintain public number: middleware interested circles , mainly published a collection of java source code reading, JUC (java and contract), Netty, ElasticJob, Mycat, Dubbo, RocketMQ, mybaits other source.

Here Insert Picture Description

Guess you like

Origin juejin.im/post/5dcaa0fd51882557486c2de6