Spring extension point 1: Application of BeanDefinitionRegistryPostProcessor in the framework

The previous note mainly recorded the implementation principle of the BeanDefinitionRegistryPostProcessor extension mechanism. This note pays attention to recording the use of the extension mechanism during the process of learning the source code.

In fact, I said in the last note that this extension mechanism is too advanced. If we inject into the spring container through the @Component annotation, then our custom implementation class is that all business beans have not been put in the spring container. At this time, it is executed. At this time, there are generally few business needs to modify beanDefinition at this time. However, in the underlying framework, this extension point may be used. What I see is that mybatis is useful at the bottom To this extension point

what does mybatis do

In fact, mybatis is a persistence layer framework. When integrating with spring, in simple terms, I think we have done one thing, and handed over the mapper interface written by our programmers to spring to manage.
But we need to think about an interface, How to leave it to spring to manage? I think one of the design ideas here is worth learning: For spring, it will not modify its own code in order to integrate mybatis. Will the original mybatis be modified? Of course not, because if mybatis is only to integrate spring and modify its own code, then mybatis is not mybatis. If you want to combine the two, there must always be an intermediate glue. At this time, spring said, since you want to integrate with me, then you should expand a jar package and be responsible for integrating with my spring. You You can use the extension points that I exposed to you. In short, I don’t care what you do. You just need to put your interface in my beanDefinitionMap, and it’s fine. I am responsible for initializing and putting it in the container. As for how you put it Put the mapper interface in my beanDefinitionMap, I don’t care
. At this time, mybatis will implement a jar package by itself: mybatis-spring-1.3.2.jar
is in this jar package. Mybatis uses spring's BeanDefinitionRegistryPostProcessor extension point , To put the mapper interface provided by our programmers into beanDefinitionMap

Mybatis integrates spring principles

When we use mybatis, we need to add an annotation, @MapperScan. In this annotation, specify the path to be scanned. Who is this path for? It is to give us the implementation class MapperScannerConfigurer of BeanDefinitionRegistryPostProcessor that we want to learn in this note
Insert picture description here

@MapperScan

In this annotation, a class MapperScannerRegistrar is introduced through @Import,

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware 

This class is the implementation class of ImportBeanDefinitionRegistrar, which is also one of the spring extension points. We will not explain in depth here. We can simply understand that during the initialization process, spring will call the method of the interface implementation class
in mybatis-spring-1.3 In the .x.jar package, we scanned directly in the registerBeanDefinitions() method of MapperScannerRegistrar to scan the mapper interface declared by ourselves.
But in the mybatis-spring-2.x.jar package, in the registerBeanDefinitions() method of MapperScannerRegistrar In the implementation class of a BeanDefinitionRegistryPostProcessor registered in beanDefinitionMap: MapperScannerConfigurer

Therefore, in the 1.x version, when scanning, the extension point ImportBeanDefinitionRegistrar is used, but in the 2.x package, the extension point of BeanDefinitionRegistryPostProcessor is used

Let's take a look at how mapper scanning is performed in the 2.X version

FoldersScannerConfigure

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

        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        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, ",; \t\n"));
    }

As you can see, in its method, it will declare a scanner by itself, and then call the scan method to scan. As of this point, we can end it. We have seen it. In mybatis-spring-2.x.jar, it is used. The mechanism of BeanDefinitionRegistryPostProcessor to scan the mapper

Subsequent processing after scanning

Since the ClassPathMapperScanner here is mybatis's own, so when the scan method is called, it will call its own scan method

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            this.processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

The scanning method here is actually almost the same as spring's own scanning bean, but after scanning the corresponding class file under the package, the judgment conditions are different, mybatis will judge whether the class is an interface, etc.

After scanning, there is a very important operation: processBeanDefinitions()

Modify the properties of beanDefinition

We need to consider, why do we need to modify the corresponding beanDefinition attribute after scanning the interface?
First, let's talk about what changes have been made in the processBeanDefinitions() method

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

      // 1.添加一个构造函数
	  // 2.将beanDefinition的beanClass设置为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() + "'.");
        }
		// 3.将beanDefinition的自动注入属性设置为byType
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
}

Here we can think that there are two key points:
1. Set the beanClass of beanDefinition to mapperFactoryBean
2. Set the autowireMode of beanDefinition to byType

We consider the first point, why set beanClass to mapperFactoryBean?
You can think of an interface, how do we inject it into the service? The interface has no business logic, so it can only inject a dynamic proxy object, so the mapperFactoryBean here is to generate a proxy object of the mapper interface when the mapper interface is injected into the service, and inject it into the service, here and the mapperFactoryBean The specific logic is related and will not be explained in depth

Then consider the second point: why the automatic injection model is set to byType. It should be noted that byType and @Autowired's byType are two completely different concepts. As I explained in the previous blog, if the automatic injection model is byType, then When we inject, we don’t need to add any annotations to the attributes, we only need to provide the corresponding set method, and the input of the set method is the type we want to inject.
That’s why we set it to byType. We want Next, if it is not set to byType, then when injecting sqlSessionFactory or sqlSessionTemplate into the mapper interface, the @Autowired annotation needs to be provided. Then, isn't this strongly coupled with spring? So if autoWireMode is set to byType, there is no need to strongly couple with spring

to sum up

Therefore, when the entire note is written down, we also know that when spring and mybatis are integrated, not only the extension point of factoryBean and automatic injection model is used, but also the extension mechanism of BeanDefinitionRegistryPostProcessor is used when scanning the mapper interface.

Guess you like

Origin blog.csdn.net/CPLASF_/article/details/115023146