Spring Boot默认Initializer(1)——ConfigurationWarningsApplicationContextInitializer
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); } } }