本节将对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。