购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.9 ConfigurationWarningsApplicationContextInitializer分析

本节将对ConfigurationWarningsApplicationContextInitializer进行分析,关于该对象的基础定义代码如下:

public class ConfigurationWarningsApplicationContextInitializer
      implements ApplicationContextInitializer<ConfigurableApplicationContext> {}

从对象的基础定义中可以发现并未实现其他接口,下面对ApplicationContextInitializer的实现方法进行说明,详细代码如下:

public void initialize(ConfigurableApplicationContext context) {
     context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(
getChecks()));
    }

在这段代码中添加了Bean工厂后置处理器,具体的类型是ConfigurationWarningsPostProcessor。关于ConfigurationWarningsPostProcessor的分析主要关注postProcessBeanDefinitionRegistry方法,具体处理代码如下:

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
        for (Check check :this.checks) {
           // 获取检查结果
           String message = check.getWarning(registry);
           if (StringUtils.hasLength(message)) {
               warn(message);
           }
        }
    }

在上述代码中会通过成员变量checks中的元素来检查数据,当检查结果存在时会输出一次warn级别的日志。

在前文对ConfigurationWarningsPostProcessor分析时提到了Check,本节将对该接口的实现类进行分析,关于Check的定义代码如下:

@FunctionalInterface
protected interface Check {
   String getWarning(BeanDefinitionRegistry registry);
}

在Check中提供了getWarning方法需要进行实现,该方法用于获取警告信息,具体在Spring Boot中它的实现类是ComponentScanPackageCheck,实现方法代码如下:

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, ", ") + ".";
}

在getWarning方法中主要的处理流程如下:

(1)获取组件所在的包路径;

(2)获取有问题的包路径;

(3)如果存在有问题的包路径,将信息组装后返回。

在上述处理过程中主要有两个方法来负责,第一个方法是getComponentScanningPackages,具体处理代码如下;

在getComponentScanningPackages方法中主要的处理流程如下:

(1)创建存储集合。

(2)从Bean注册接口中获取所有的Bean定义名称。

(3)将获取的Bean名称逐一处理,处理细节如下:

①从Bean注册接口中获取Bean的定义对象;

②判断Bean定义对象是否是AnnotatedBeanDefinition类型,如果是会进行Bean所在包路径的确定,确定后加入返回结果中。

在上述处理流程中最关键的处理方法是addComponentScanningPackages,该方法用于获取注解ComponentScan的数据,将数据结果放入包路径集合中,具体处理代码如下:

    private void addComponentScanningPackages(Set<String> packages, AnnotationMetadata
metadata) {
        AnnotationAttributes attributes = AnnotationAttributes
               .fromMap(metadata.getAnnotationAttributes(ComponentScan.class.getName(),
true));
        if (attributes != null) {
           addPackages(packages, attributes.getStringArray("value"));
           addPackages(packages, attributes.getStringArray("basePackages"));
           addClasses(packages, attributes.getStringArray("basePackageClasses"));
           if (packages.isEmpty()) {
               packages.add(ClassUtils.getPackageName(metadata.getClassName()));
           }
        }
    }

在addComponentScanningPackages方法中,会提取注解元数据中关于ComponentScan注解的数据信息,将value、basePackages和basePackageClasses三个数据信息加入集合中。在扫描完成所有的包路径后会进行getProblematicPackages方法的调度,该方法用于获取有问题的包路径,具体处理代码如下:

private List<String> getProblematicPackages(Set<String> scannedPackages) {
   List<String> problematicPackages = new ArrayList<>();
   for (String scannedPackage :scannedPackages) {
      // 判断是否出现问题
      if (isProblematicPackage(scannedPackage)) {
          problematicPackages.add(getDisplayName(scannedPackage));
      }
   }
   return problematicPackages;
}

在getProblematicPackages方法中主要关注isProblematicPackage方法,该方法用于判断是否出现问题,具体判断代码如下:

private boolean isProblematicPackage(String scannedPackage) {
   if (scannedPackage == null || scannedPackage.isEmpty()) {
      return true;
   }
   return PROBLEM_PACKAGES.contains(scannedPackage);
}

在这段代码中判断出现问题的依据有以下两个:

(1)满足包路径为null或者包路径元素不存在;

(2)包路径包含关键字org或者org.springframework。 VAMvLivxcWz+lGXwCK6ZTUcF5f1VpB8e5XMJkzfGCMcsvg+I01mJ46GE63XWq06F

点击中间区域
呼出菜单
上一章
目录
下一章
×