Tip: after the article is written, the directory can be generated automatically. Please refer to the help document on the right for how to generate it
preface
Last article Referring to the postProcessBeanDefinitionRegistry() method of mappercannerconfigurer, this article continues to delve into this method.
1, Mapper registration process
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { //Omit some codes scanner.scan( StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); // Focus on doScan() doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
public Set<BeanDefinitionHolder> doScan(String... basePackages) { // Call the doScan() method of the parent class, scan all mappers according to the path configured in @ MapperScan, and encapsulate them into BeanDefinitionHolder 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 { // Processing the scanned bd is the most critical step processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
Here we have obtained all mappers and encapsulated them into BeanDefinitionHolder. The next step is the most critical step in dealing with BeanDefinitionHolder. Let's see how to deal with it.
Example: pandas is a NumPy based tool created to solve data analysis tasks.
2, processBeanDefinitions() parsing process
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { // Omit some codes String beanClassName = definition.getBeanClassName(); definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); definition.setBeanClass(this.mapperFactoryBeanClass); definition.getPropertyValues().add("addToConfig", this.addToConfig); // Omit some codes } }
All mappers will be traversed in this method. There are two key steps:
1. Add the parameter value of a constructor: beanClassName, that is, the fully qualified class name of the current Mapper.
2. Set bd's BeanClass to MapperFactoryBean Class, there's a wave of cheating here. Why do you do this? Then you have to look at MapperFactoryBean.
3, MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig = true; public MapperFactoryBean() { // intentionally empty } public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } @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(); } } } @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } // Omit some codes }
1.FactoryBean
MapperFactoryBean implements FactoryBean. As we all know, when spring's getBean() method is called, if the current object is of FactoryBean type, spring will call its getObject() method. You can see that the getObject() of MapperFactoryBean is actually the processing logic of Mybatis;
2.InitializingBean
MapperFactoryBean also inherits SqlSessionDaoSupport. See the specific inheritance relationship below:
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>
public abstract class SqlSessionDaoSupport extends DaoSupport
public abstract class DaoSupport implements InitializingBean
That is to say, MapperFactoryBean is of InitializingBean type. When spring initializes an object, if the current object is of InitializingBean type, spring will call its afterpropertieset () method.
public abstract class DaoSupport implements InitializingBean { /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); @Override public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException { // Here is the template mode. The specific processing logic is placed in the subclass implementation checkDaoConfig(); // Omit some codes } // Omit some codes }
Let's look at the implementation of MapperFactoryBean:
@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(); } } }
You can see that MapperFactoryBean will first try to get Mapper here (of course, it can't get it). If it can't get it, it will call configuration.addMapper(this.mapperInterface), which is the processing logic of Mybatis.
3.this.mapperInterface
this. Where did mapperinterface come from? The db processing mentioned earlier is useful:
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
Take a look at the MapperFactoryBean constructor:
public MapperFactoryBean() { // intentionally empty } public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; }
See everything here clearly.
summary
Here, the parsing of @ MapperScan is completed. In fact, the process is not complicated. First, follow the scan package path we configured to obtain all mappers and package them into the BeanDefinitionHolder set, then traverse the whole set, set the Mapper type to MapperFactoryBean, and use MapperFactoryBean to complete the operation of Mybatis.
The above is only my own understanding. If there is anything wrong, please correct it and learn and make progress together.