Задокументируйте тайну конфигурации Mybatis

Spring Boot задняя часть Байду MyBatis
Задокументируйте тайну конфигурации Mybatis

За каждым явлением стоит причина.Чем причудливее ошибка, тем незаметнее детали.За каждой ошибкой стоит принцип и механизм работы фреймворка или кода.Чтобы устранить ошибку, нужно не только искать в сети , но также необходимо понять и обобщить лежащие в его основе принципы.  Мой коллега недавно изучает и использует Mybatis.Он использует MapperScannerConfigurer Mybatis для связанной конфигурации и надеется указать атрибуты, такие как basePackage и mappers, через конфигурацию yml. Для этого написан собственный класс конфигурацииStarterAutoConfigurationи пользовательский класс атрибутовTkProperties, а при инициализацииMapperScannerConfigurerиспользовать, когдаTkPropertiesсвойства в . Однако это имеет неприятные последствия при инициализацииMapperScannerConfigurerчас,TkPropertiesАтрибуты экземпляра живут и умирают в неинициализированном состоянии.

  С этой целью мы потратили много времени на изучение причины, и, наконец, нам пришлось спросить другого крупного парня, только чтобы обнаружить, что за этой странной проблемой стоит такая-то причина.  Давайте сначала посмотрим на большого парня оMapperScannerConfigurerреализация пользовательской конфигурации. Сначала он определяет пользовательский класс конфигурацииBkStarterAutoConfiguration,использовать@EnableConfigurationPropertiesАннотации будутTkPropertiesОбъявлен как класс свойств конфигурации.

@Configuration
@EnableConfigurationProperties({TkProperties.class})
@AutoConfigureBefore(MybatisAutoConfiguration.class)
public class BkStarterAutoConfiguration {
  @Bean
  @ConditionalOnMissingBean
  @Order(Ordered.HIGHEST_PRECEDENCE)
  public TkProperties tkProperties() {
    return new TkProperties();
  }
}

  НижеTkPropertiesопределение, используя@ConfigurationPropertiesАннотация объявляет префикс конфигурации атрибута, и два имени атрибутаbasePackageа такжеmappers.

@Data
@ConfigurationProperties(prefix = "tk")
public class TkProperties {
  private String basePackage;
  private String mappers;
}

MapperConfigобъявляется и настраиваетсяMapperScannerConfigurerКласс конфигурации экземпляра, используя@BeanАннотированныйmapperScannerConfigurerметод для инициализации, параметры метода которогоTkProperties.

@Configuration
public class MapperConfig {
  @Bean
  public MapperScannerConfigurer mapperScannerConfigurer(TkProperties tkProperties) {
    MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
    mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
    //使用TkProperties的成员变量来配置mapperScannerConfigurer
    mapperScannerConfigurer.setBasePackage(tkProperties.getBasePackage());
    Properties properties = new Properties();
    properties.setProperty("mappers", tkProperties.getMappers());
    mapperScannerConfigurer.setProperties(properties);
    return mapperScannerConfigurer;
  }
}

Файл конфигурации  yml показан ниже.

---
tk:
  basePackage: cn.remcarpediem.mybatis.dao
  mappers: cn.remcarpediem.mappers.BaseDao

  На первый взгляд кажется, что в коде нет проблем, но во время выполнения, когда инициализируется экземпляр MapperScannerConfigurer, свойства экземпляра TkProperties не инициализируются успешно.

运行调试图

  Должно быть много хорошо информированных читателей, которые уже знают причины этого явления. "Убийца" этоMapperScannerConfigurerРеализованный интерфейсBeanDefinitionRegistryPostProcessor. Нам все еще нужно медленно объяснять конкретные причины, потому что это включает в себя многие принципы Spring Boot.

первый,BeanDefinitionRegistryPostProcessorинтерфейс наследуетBeanFactoryPostProcessorИнтерфейс, все в целом правыBeanFactoryPostProcessorБолее знакомо, что это постпроцессор (PostProcessor) фабрики экземпляров (BeanFactory), и он похож на постпроцессор экземпляра (BeanPostProcessor).BeanFactoryPostProcessorВ файле определен только один метод.ApplicationContextВнутреннийBeanFactoryзагруженBeanDefinitionпосле, но до создания экземпляра bean-компонента. Поэтому обычно мы можем реализовать этот интерфейс для создания экземпляра предыдущегоBeanDefinitionмодифицировать. НапримерPropertySourcesPlaceholderConfigurerпросто поймиBeanFactoryPostProcessorинтерфейс, который используется для обработки@ValueАннотируйте измененную переменную и измените ее значение.

public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

иBeanDefinitionRegistryPostProcessorИнтерфейс простирается отBeanFactoryPostProcessor,этоBeanDefinitionRegistryпостпроцессор, это может бытьBeanFactoryPostProcessorЗарегистрируйте некоторые специальные до обнаруженияBeanDefinition, например, можно зарегистрировать для определенияBeanFactoryPostProcessorизBeanDefintion, как мы упоминали ранееMapperScannerConfigurerа такжеConfigurationClassPostProcessor.

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

MapperScannerConfigurerизpostProcessBeanDefinitionRegistryв основном используетсяClassPathMapperScannerсканироватьMybatisизMapper.ClassPathMapperScannerнаследоватьClassPathBeanDefinitionScanner,существуетdoScanполученный методомbasePackageВсе по указанному пути пакетаMapperизBeanDefinition, а затем зарегистрируйтесь.

иBeanPostProcessorЭто постпроцессор экземпляра Bean. Перед инициализацией каждого экземпляра компонента егоpostProcessBeforeInitializationметод и вызвать его после инициализацииpostProcessAfterInitializationметод.ConfigurationPropertiesBindingPostProcessorДостигнутоBeanPostProcessorинтерфейс для работы с@ConfigurationPropertiesМодифицированный экземпляр.

public interface BeanPostProcessor {
  Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
  Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

 Мы можем подвести итогBeanDefinitionRegistryPostProcessor,BeanFactoryPostProcessorа такжеBeanPostProcessorПорядок и время действий трех постпроцессоров.

三种后处理器的顺序

Отсюда мы также можем понять, почемуMapperScannerConfigurerПри инициализацииTkPropertiesне был инициализирован, потому чтоConfigurationPropertiesBindingPostProcessorне был инициализирован и неTkPropertiesобрабатывать.

 Если вы столкнулись с проблемами и ошибками, не просто нажмите на Baidu, чтобы решить проблему, а глубоко поймите механизм и принцип, лежащий в ее основе.Я надеюсь, что каждый сможет изучить более глубокие принципы и получить больше знаний.