内容简介:每个现象背后都有其缘由,越离奇的bug越是由不起眼的细节引发,每个bug背后都有框架或代码运行的原理和机制所在,解决bug,不仅仅需要去网上查询,还需要对其背后的原理进行了解和总结。 同事大佬最近在学习并使用Mybatis,他使用Mybatis的MapperScannerConfigurer来进行相关配置,并希望通过yml配置来指定basePackage,mappers等属性。为此,编写了自定义的配置类为此,我们花了大量时间探查缘由,最后不得不询问了另一位大佬,才发现这个离奇问题的背后竟然有着这样的缘由。
每个现象背后都有其缘由,越离奇的bug越是由不起眼的细节引发,每个bug背后都有框架或代码运行的原理和机制所在,解决bug,不仅仅需要去网上查询,还需要对其背后的原理进行了解和总结。 同事大佬最近在学习并使用Mybatis,他使用Mybatis的MapperScannerConfigurer来进行相关配置,并希望通过yml配置来指定basePackage,mappers等属性。为此,编写了自定义的配置类 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
较为熟悉,它是实例工厂(BeanFactory)的后处理器(PostProcessor),与之类似的还有实例的后处理器(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实例的后处理器。每个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
进行处理 。
遇到问题和bug,不要百度一下解决方案处理就结束了,而是要深入了解一下背后的机制和原理,希望大家都能够多多探索更加深入的原理,获得更多的知识。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
RESTful Web Services Cookbook
Subbu Allamaraju / Yahoo Press / 2010-3-11 / USD 39.99
While the REST design philosophy has captured the imagination of web and enterprise developers alike, using this approach to develop real web services is no picnic. This cookbook includes more than 10......一起来看看 《RESTful Web Services Cookbook》 这本书的介绍吧!