1.問題描述
由于自己的mapper沒有放在本項目下,而是通過jar包的方式引入進來的,是以需要在啟動的時候配置MapperScannerConfigurer,然後指定包路徑;為了支援可配置化,将包路徑配置在了yml檔案中,然後啟動容器,結果。。。
2.上代碼
配置類
@Configuration
//@MapperScan(basePackages = {"io.db.api.dao"}) // 增加報掃描配置
public class MybatisPlusConfig {
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(@Value("${mybatis.basePackage}") String basePackage) {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage(basePackage);
return mapperScannerConfigurer;
}
}
yml配置檔案
mybatis:
basePackage: io.db.api.dao
這裡采用@Value("${mybatis.basePackage}")的方式從配置檔案中讀取包的全路徑。
啟動報錯
java.lang.IllegalArgumentException: Could not resolve placeholder 'mybatis.basePackage' in value "${mybatis.basePackage}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:172)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:237)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:211)
at org.springframework.core.env.AbstractEnvironment.resolveRequiredPlaceholders(AbstractEnvironment.java:575)
.....................................................
很顯然,當bean初始化的時候,占位符并沒有被解析。
3.源碼分析
Spring的解析占位符,是通過PropertySourcesPlaceholderConfigurer這個類來解析的,而PropertySourcesPlaceholderConfigurer繼承了BeanFactoryPostProcessor。
關于BeanFactoryPostProcessor官方給出的解釋是:操作bean配置中繼資料。Spring IoC容器允許BeanFactoryPostProcessor讀取配置中繼資料,并可能在容器執行個體化除執行個體之外的任何bean 之前更改它。
也就是說,在BeanDefinition建立完成後,Bean執行個體化之前通過BeanFactoryPostProcessor來修改BeanDefinition的參數。
再來分析 MapperScannerConfigurer,這個類實作了BeanDefinitionRegistryPostProcessor,同時BeanDefinitionRegistryPostProcessor繼承了BeanFactoryPostProcessor,也就是說MapperScannerConfigurer也是一個mybatis自定義的BeanFactoryPostProcessor。
那麼為什麼會加載不到呢,我們來通過源碼分析兩者的加載順序問題
首先看AbstractApplicationContext的refreash()方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 初始化環境
prepareRefresh();
// 通過子類建立BanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 準備BanFactory
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// 回調BeanFactoryPostProcessor中的方法
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
.......
}
catch (BeansException ex) {
.......
}
finally {
.......
}
}
}
這是IOC容器初始化的主要流程,這裡主要關注invokeBeanFactoryPostProcessors(beanFactory)這個方法,這個方法主要是執行BeanFactoryPostProcessor,代碼如下:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//具體的BeanFactoryPostProcessors執行邏輯
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
進入PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()這個方法中
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
// 預設為DefaultListableBeanFactory 實作了BeanDefinitionRegistry
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 先執行内置的BeanDefinitionRegistryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 從BeanFactory加載好的BeanDefinition中擷取實作了 PriorityOrdered 接口或注解的BeanDefinitionRegistryPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 執行BeanDefinitionRegistryPostProcessor
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 從BeanFactory加載好的BeanDefinition中擷取實作了 Ordered 接口或注解的BeanDefinitionRegistryPostProcessor
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 根據Order排列執行順序
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 執行BeanDefinitionRegistryPostProcessor
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
// 執行剩餘的BeanDefinitionRegistryPostProcessors
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
//調用postProcessBeanFactory回調
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
..................
}
4.得出結論
根據可以看出BeanDefinitionRegistryPostProcessors是要優先于BeanFactoryProcessor執行的,先執行BeanDefinitionRegistryPostProcessors中的方法,最後再統一執行BeanFactoryProcessor。
由于MapperScannerConfigurer是實作的BeanDefinitionRegistryPostProcessors比PropertySourcesPlaceholderConfigurer要先執行,是以在執行過程中占位符并未得到解析,故抛出異常。
5.解決方案
自定義一個占位符解析類繼承PropertySourcesPlaceholderConfigurer以及實作BeanDefinitionRegistryPostProcessors,然後重寫postProcessBeanDefinitionRegistry方法,在方法體中調用spring的占位符解析邏輯;聲明為@Configuration并且指定Order 確定其在MapperScannerConfigurer之前能夠執行
或者不采用配置的方式讀取basePackage
@Configuration
@Order(value = 1)
public class CustomPropertySourcesPlaceholder extends PropertySourcesPlaceholderConfigurer
implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(@NonNull BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 占位符解析
postProcessBeanFactory((ConfigurableListableBeanFactory) beanDefinitionRegistry);
}
}
如有不足和錯誤的地方歡迎來指正,謝謝!