ConfigurationWarningsApplicationContextInitializer的作用是用來報告Spring容器的一些常見的錯誤配置的。這個類中定義了兩個內部類:
1. 定義了一個Check接口及它的實現類ComponentScanPackageCheck(以靜態內部類形式定義)
2. 定義了一個BeanDefinitionRegistryPostProcessor接口的實現類(以靜態內部類形式定義)
因此我把這兩個內部類理解成ConfigurationWarningsApplicationContextInitializer的組合成員,類圖附在最后,
initialize()方法,獲取Check的實例,然后構建出一個ConfigurationWarningsPostProcessor實例,注冊到Sring的容器中
public void initialize(ConfigurableApplicationContext context) { context.addBeanFactoryPostProcessor( new ConfigurationWarningsPostProcessor(getChecks())); }
ConfigurationWarningsPostProcessor是BeanDefinitionRegistryPostProcessor接口的實現類,而BeanDefinitionRegistryPostProcessor接口又繼承於BeanFactoryPostProcessor接口,下面依次了解下這幾個Spring的接口和類圖:
BeanFactoryPostProcessor接口:允許對spring容器的bean definition進行自定義的修改,可改變容器底層管理的bean的屬性值。Spring容器會自動檢測容器的bean definition中有沒實現了BeanFactoryPostProcessor接口的Bean ,如果有話將會在創建其他Bean之前首先執行該接口的代碼。
BeanDefinitionRegistryPostProcessor接口:對BeanFactoryPostProcessor接口的一個擴展,允許在Spring容器會自動檢測容器的bean definition之前,進一步的注冊bean definiton到容器中。特定情況下還可以通過進一步的注冊bean definiton而反過來定義BeanFactoryPostProcessor接口的實例
BeanDefinitionRegistry接口:作用主要是向注冊表中注冊 BeanDefinition 實例
搞清楚相關接口的用意后,我們看一下 ConfigurationWarningsPostProcessor 實現BeanDefinitionRegistryPostProcessor接口和BeanFactoryPostProcessor接口做了哪些事情,主要是看兩個方法:
//這是實現BeanFactoryPostProcessor接口的方法,沒做任何事 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } //這個是實現BeanDefinitionRegistryPostProcessor接口的方法,主要是把在注冊BeanDefinition實例過程中產生的告警信息傳給Check接口的實例進行處理產生要告警的內容,沒進行告警輸出 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { for (Check check : this.checks) { String message = check.getWarning(registry); if (StringUtils.hasLength(message)) { warn(message); //通過日志打印出告警信息 } } }
由此可見 ConfigurationWarningsPostProcessor 的主要作用就是把在注冊BeanDefinition實例過程中產生的告警信息傳給Check接口的實例進行處理,ConfigurationWarningsApplicationContextInitializer中只提供了一個Check的實現 ComponentScanPackageCheck,簡單分析下 ComponentScanPackageCheck類的源碼:
//這個方法是實現了Check接口的接口方法 public String getWarning(BeanDefinitionRegistry registry) { Set<String> scannedPackages = getComponentScanningPackages(registry); List<String> problematicPackages = getProblematicPackages(scannedPackages); //獲取有在掃描包范圍內的有警告的包 if (problematicPackages.isEmpty()) { return null; } return "Your ApplicationContext is unlikely to " + "start due to a @ComponentScan of " + StringUtils.collectionToDelimitedString(problematicPackages, ", ") + "."; }
//獲取Spring容器要掃描的包 protected Set<String> getComponentScanningPackages( BeanDefinitionRegistry registry) { Set<String> packages = new LinkedHashSet<String>(); String[] names = registry.getBeanDefinitionNames(); for (String name : names) { BeanDefinition definition = registry.getBeanDefinition(name); if (definition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedDefinition = (AnnotatedBeanDefinition) definition; addComponentScanningPackages(packages, annotatedDefinition.getMetadata()); //從每個definition中獲取要掃描的包,並加入到packages集合中 } } return packages; }
以下是打印告警信息的方法,這個方法 還是位於 ConfigurationWarningsPostProcessor 類中,這個方法會由Spring容器在檢測處理BeanFactoryPostProcessor接口的bean definition時觸發並執行:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { for (Check check : this.checks) { String message = check.getWarning(registry); if (StringUtils.hasLength(message)) { warn(message); } } }