天天看點

Spring源碼分析springboot啟動加載MapperScannerConfigurer讀取不到配置檔案屬性的問題

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

如有不足和錯誤的地方歡迎來指正,謝謝!