Punto de extensión de primavera 1: Aplicación de BeanDefinitionRegistryPostProcessor en el marco

La nota anterior registró principalmente el principio de implementación del mecanismo de extensión BeanDefinitionRegistryPostProcessor. Esta nota presta atención al registro del uso del mecanismo de extensión durante el proceso de aprendizaje del código fuente.

De hecho, dije en la última nota que este mecanismo de extensión es demasiado avanzado.Si inyectamos en el contenedor de resorte a través de la anotación @Component, entonces nuestra clase de implementación personalizada es que no se han colocado todos los beans de negocios en el contenedor de resorte. esta vez, se ejecuta. En este momento, generalmente hay pocas necesidades comerciales para modificar beanDefinition en este momento. Sin embargo, en el marco subyacente, se puede usar este punto de extensión. Lo que veo es que mybatis es útil en la parte inferior Para este punto de extensión

que hace mybatis

De hecho, mybatis es un marco de capa de persistencia. Al integrarse con Spring, en términos simples, creo que ha hecho una cosa, y entregó la interfaz del mapeador escrita por nuestros programadores a Spring para su administración.
Pero tenemos que pensar en una interfaz , ¿Cómo dejar que la primavera se las arregle? Creo que vale la pena aprender una de las ideas de diseño aquí: para la primavera, no modificará su propio código para integrar mybatis. ¿Se modificará el mybatis original? Por supuesto que no, porque si mybatis es solo para integrar spring y modificar su propio código, entonces mybatis no es mybatis, entonces, ¿qué debo hacer? Si quieres combinar los dos, siempre debe haber un pegamento intermedio. En este momento, dijo Spring, ya que quieres integrarte conmigo, entonces debes expandir un paquete de jarra y ser el responsable de integrar con mi resorte. Tú puedes usa los puntos de extensión que te expuse. En resumen, no me importa lo que hagas. Solo necesitas poner tu interfaz en mi beanDefinitionMap, y yo seré responsable de inicializarlo y ponerlo en el contenedor. En cuanto a cómo lo pones Pon la interfaz del mapeador en mi beanDefinitionMap, no me importa
. En este momento, mybatis implementará un paquete jar por sí mismo: mybatis-spring-1.3.2.jar
está en este paquete jar. Mybatis usa BeanDefinitionRegistryPostProcessor de spring punto de extensión, para poner la interfaz del mapeador proporcionada por nuestros programadores en beanDefinitionMap

Mybatis integra principios de primavera

Cuando usamos mybatis, necesitamos agregar una anotación, @MapperScan. En esta anotación, especifique la ruta a escanear. ¿Para quién es esta ruta? Es para darnos la clase de implementación MapperScannerConfigurer de BeanDefinitionRegistryPostProcessor que queremos aprender en esta nota
Inserte la descripción de la imagen aquí

@MapperScan

En esta anotación, se introduce una clase MapperScannerRegistrar a través de @Import,

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware 

Esta clase es la clase de implementación de ImportBeanDefinitionRegistrar, que también es uno de los puntos de extensión de Spring. No explicaremos en profundidad aquí. Simplemente podemos entender que durante el proceso de inicialización, Spring llamará al método de la clase de implementación de interfaz
en mybatis- spring-1.3 En el paquete .x.jar, escaneamos directamente en el método registerBeanDefinitions () de MapperScannerRegistrar para escanear la interfaz del mapeador declarada por nosotros mismos.
Pero en el paquete mybatis-spring-2.x.jar, en el registerBeanDefinitions () método de MapperScannerRegistrar En la clase de implementación de un BeanDefinitionRegistryPostProcessor registrado en beanDefinitionMap: MapperScannerConfigurer

Por lo tanto, en la versión 1.x, al escanear, se usa el punto de extensión ImportBeanDefinitionRegistrar, pero en el paquete 2.x, se usa el punto de extensión de BeanDefinitionRegistryPostProcessor

Echemos un vistazo a cómo se realiza el escaneo del mapeador en la versión 2.X

CarpetasEscánerConfigurar

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

Como puede ver, en su método, declarará un escáner por sí mismo, y luego llamará al método de escaneo para escanear. A partir de este punto, podemos finalizarlo. Lo hemos visto. En mybatis-spring-2.x. jar, se utiliza. El mecanismo de BeanDefinitionRegistryPostProcessor para escanear el asignador

Procesamiento posterior después de escanear

Dado que el ClassPathMapperScanner aquí es de mybatis, cuando se llama al método de escaneo, llamará a su propio método de escaneo

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

El método de escaneo aquí es en realidad casi el mismo que el propio bean de escaneo de Spring, pero después de escanear el archivo de clase correspondiente en el paquete, las condiciones de juicio son diferentes, mybatis juzgará si la clase es una interfaz, etc.

Después de escanear, hay una operación muy importante: processBeanDefinitions ()

Modificar las propiedades de beanDefinition

Debemos considerar, ¿por qué necesitamos modificar el atributo beanDefinition correspondiente después de escanear la interfaz?
Primero, hablemos de los cambios que se han realizado en el método 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");
      }

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

Aquí podemos pensar que hay dos puntos clave:
1. Establecer beanClass de beanDefinition en mapperFactoryBean
2. Establecer autowireMode de beanDefinition en byType

Consideramos el primer punto, ¿por qué establecer beanClass en mapperFactoryBean?
Puedes pensar en una interfaz, ¿cómo la inyectamos en el servicio? La interfaz no tiene lógica comercial, por lo que solo puede inyectar un objeto proxy dinámico, por lo que el mapperFactoryBean aquí es para generar un objeto proxy de la interfaz del asignador cuando la interfaz del asignador se inyecta en el servicio, e inyectarlo en el servicio, aquí y the mapperFactoryBean La lógica específica está relacionada y no se explicará en profundidad

Luego considere el segundo punto: por qué el modelo de inyección automática está configurado en byType. Cabe señalar que byType y @ Autowired's byType son dos conceptos completamente diferentes. Como expliqué en el blog anterior, si el modelo de inyección automática es byType, entonces cuando inyectamos, no necesitamos agregar ninguna anotación a los atributos, solo necesitamos proporcionar el método set correspondiente, y la entrada del método set es el tipo que queremos inyectar.
Es por eso que lo configuramos en byType, queremos Siguiente, si no está configurado en byType, entonces, al inyectar sqlSessionFactory o sqlSessionTemplate en la interfaz del mapeador, se debe proporcionar la anotación @Autowired. Entonces, ¿no está esto fuertemente acoplado con la primavera? Entonces, si autoWireMode está configurado en byType, no es necesario unir fuertemente con spring

para resumir

Por lo tanto, cuando se escribe toda la nota, también sabemos que cuando se integran spring y mybatis, no solo se usa el punto de extensión de factoryBean y el modelo de inyección automática, sino que también se usa el mecanismo de extensión de BeanDefinitionRegistryPostProcessor al escanear la interfaz del mapeador.

Supongo que te gusta

Origin blog.csdn.net/CPLASF_/article/details/115023146
Recomendado
Clasificación