本篇文章是对上篇prepareContext的补充,在该方法的执行过程中,遍历了最初从META-INF/spring.factories文件中加载到的ApplicationContextInitializer,依次调用了其initialize方法
回顾第一篇文章,SpringApplication初始化的时候,一共加载到了6个内置的ApplicationContextInitializer,本篇文章就逐一分析每个内置的初始化器做了哪些事情
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
ConfigurationWarningsApplicationContextInitializer
这个类的作用是检查扫描路径是否合法,对非法的扫描路径,在控制台打印一行警告日志
直接看它的initialize方法
public void initialize(ConfigurableApplicationContext context) {
context.addBeanFactoryPostProcessor(new ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor(this.getChecks()));
}
往容器中添加了一个BeanFactoryPostProcessor,类型是其内部类ConfigurationWarningsPostProcessor,这个类实际上实现了BeanDefinitionRegistryPostProcessor 接口,它是BeanFactoryPostProcessor的子类
protected static final class ConfigurationWarningsPostProcessor implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {
private ConfigurationWarningsApplicationContextInitializer.Check[] checks;
public ConfigurationWarningsPostProcessor(ConfigurationWarningsApplicationContextInitializer.Check[] checks) {
this.checks = checks;
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
ConfigurationWarningsApplicationContextInitializer.Check[] var2 = this.checks;
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
ConfigurationWarningsApplicationContextInitializer.Check check = var2[var4];
String message = check.getWarning(registry);
if (StringUtils.hasLength(message)) {
this.warn(message);
}
}
}
......
}
在Spring容器执行refresh的时候,最开始就会先处理BeanFactoryPostProcessor,很多中间件都是依赖这个扩展点和Spring容器整合在一起的,而在这个处理过程中,又会优先处理子类BeanDefinitionRegistryPostProcessor 的回调,再处理父类BeanFactoryPostProcessor的回调,也就是先执行postProcessBeanDefinitionRegistry方法,再执行postProcessBeanFactory方法,这块涉及到Spring容器刷新的流程,之前我们也说过本系列文章重点在于SpringBoot在Spring基础上的扩展,所以这块内容就不详细展开了
而该类的postProcessBeanFactory方法为空,所以我们只需要看postProcessBeanDefinitionRegistry的实现就行了,它遍历了内部的Check数组,依次调用了其getWarning方法,这个Check数组,是在构造函数中传进来的,调用了内部的getChecks()方法
首先看下Check的定义,它也是当前初始化器内部定义的函数式接口
@FunctionalInterface
protected interface Check {
String getWarning(BeanDefinitionRegistry registry);
}
getChecks()方法返回的Check数组只包含一个元素ComponentScanPackageCheck,也是一个内部类
protected ConfigurationWarningsApplicationContextInitializer.Check[] getChecks() {
return new ConfigurationWarningsApplicationContextInitializer.Check[]{new ConfigurationWarningsApplicationContextInitializer.ComponentScanPackageCheck()};
}
重点看下ComponentScanPackageCheck的getWarning方法实现
protected static class ComponentScanPackageCheck implements ConfigurationWarningsApplicationContextInitializer.Check {
private static final Set<String> PROBLEM_PACKAGES;
protected ComponentScanPackageCheck() {
}
public String getWarning(BeanDefinitionRegistry registry) {
Set<String> scannedPackages = this.getComponentScanningPackages(registry);
List<String> problematicPackages = this.getProblematicPackages(scannedPackages);
return problematicPackages.isEmpty() ? null : "Your ApplicationContext is unlikely to start due to a @ComponentScan of " + StringUtils.collectionToDelimitedString(problematicPackages, ", ") + ".";
}
......
......
static {
Set<String> packages = new HashSet();
packages.add("org.springframework");
packages.add("org");
PROBLEM_PACKAGES = Collections.unmodifiableSet(packages);
}
}
其内部有一个路径列表PROBLEM_PACKAGES,在静态代码块中初始化了两个元素,org和org.springframework
scannedPackages就是我们要扫描的路径,默认情况下即启动类所在的路径
然后检查PROBLEM_PACKAGES是否包含了我们的扫描路径,如果包含了,就把相关的路径拼接成字符串返回,否则返回null
在外面遍历Check调用getWarning方法的时候,如果返回的字符串不是null,就打印一行警告
private void warn(String message) {
if (ConfigurationWarningsApplicationContextInitializer.logger.isWarnEnabled()) {
ConfigurationWarningsApplicationContextInitializer.logger.warn(String.format("%n%n** WARNING ** : %s%n%n", message));
}
}
最终在控制台打印如下日志
** WARNING ** : Your ApplicationContext is unlikely to start due to a @ComponentScan of 'org'.
这里只是打印一行日志,并不会停止程序,不过实际测试下来程序是没办法正常启动的,这个路径是Spring框架本身的包路径,扫描这个包会干扰Spring正常执行流程,陷入循环,当然正常情况下我们项目的路径也不会这样定义,除非是在整活…
ContextIdApplicationContextInitializer
这个类的作用是给容器设置一个ID,其实就是我们的项目名
public void initialize(ConfigurableApplicationContext applicationContext) {
ContextIdApplicationContextInitializer.ContextId contextId = this.getContextId(applicationContext);
applicationContext.setId(contextId.getId());
applicationContext.getBeanFactory().registerSingleton(ContextIdApplicationContextInitializer.ContextId.class.getName(), contextId);
}
第一行先获取了一个容器ID对象,然后把ID属性设置到容器中,并把这个ID对象作为一个单例Bean注册到容器的单例池
我们看下这个ContextId怎么来的,进入getContextId方法
private ContextIdApplicationContextInitializer.ContextId getContextId(ConfigurableApplicationContext applicationContext) {
ApplicationContext parent = applicationContext.getParent();
return parent != null && parent.containsBean(ContextIdApplicationContextInitializer.ContextId.class.getName()) ? ((ContextIdApplicationContextInitializer.ContextId)parent.getBean(ContextIdApplicationContextInitializer.ContextId.class)).createChildId() : new ContextIdApplicationContextInitializer.ContextId(this.getApplicationId(applicationContext.getEnvironment()));
}
如果有父容器,就根据父容器的ID创建一个子容器ID,格式为 父容器ID - 子容器序号
ContextIdApplicationContextInitializer.ContextId createChildId() {
return ContextIdApplicationContextInitializer.this.new ContextId(this.id + "-" + this.children.incrementAndGet());
}
如果没有父容器,就到environment中取spring.application.name属性,没有配置的话默认为application
private String getApplicationId(ConfigurableEnvironment environment) {
String name = environment.getProperty("spring.application.name");
return StringUtils.hasText(name) ? name : "application";
}
将取到的结果作为参数传给ContextId的构造函数
class ContextId {
......
private final String id;
ContextId(String id) {
this.id = id;
}
......
}
也就是说默认情况下,这个ContextId存了我们的项目名,然后把它设置到了容器中
DelegatingApplicationContextInitializer
SpringBoot允许我们通过各种属性配置方式自定义一些ApplicationContextInitializer,它们的调用时机就是该类的initialize方法
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
List<Class<?>> initializerClasses = this.getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
this.applyInitializerClasses(context, initializerClasses);
}
}
进入getInitializerClasses方法
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
String classNames = env.getProperty("context.initializer.classes");
List<Class<?>> classes = new ArrayList();
if (StringUtils.hasLength(classNames)) {
String[] var4 = StringUtils.tokenizeToStringArray(classNames, ",");
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
String className = var4[var6];
classes.add(this.getInitializerClass(className));
}
}
return classes;
}
它从environment中找到context.initializer.classes属性,以逗号为分隔符解析成Class列表并返回
由于这里只通过getProperty方法取了一次,我们之前分析过,这个方法会从前往后遍历所有的PropertySource,取到了就立即返回,所以通过不同方式,比如启动参数、系统配置等不同途径设置的context.initializer.classes,只有优先级最高的那个会生效
然后对于找到的自定义初始化器,调用applyInitializerClasses方法
private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
Class<?> contextClass = context.getClass();
List<ApplicationContextInitializer<?>> initializers = new ArrayList();
Iterator var5 = initializerClasses.iterator();
while(var5.hasNext()) {
Class<?> initializerClass = (Class)var5.next();
initializers.add(this.instantiateInitializer(contextClass, initializerClass));
}
this.applyInitializers(context, initializers);
}
先实例化,然后调用applyInitializers方法
private void applyInitializers(ConfigurableApplicationContext context, List<ApplicationContextInitializer<?>> initializers) {
initializers.sort(new AnnotationAwareOrderComparator());
Iterator var3 = initializers.iterator();
while(var3.hasNext()) {
ApplicationContextInitializer initializer = (ApplicationContextInitializer)var3.next();
initializer.initialize(context);
}
}
最终调用了它们的initialize方法
ServerPortInfoApplicationContextInitializer
这个类本身也是个监听器,实现了ApplicationListener接口,监听的事件类型为WebServerInitializedEvent
public class ServerPortInfoApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext>,
ApplicationListener<WebServerInitializedEvent> {
它通过initialize方法将自己添加到了容器的监听器列表中
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.addApplicationListener(this);
}
SharedMetadataReaderFactoryContextInitializer
这个类跟第一个初始化器一样,也在initialize方法中添加了一个BeanFactoryPostProcessor
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.addBeanFactoryPostProcessor(new SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor());
}
具体类型为CachingMetadataReaderFactoryPostProcessor,跟第一个初始化器的逻辑一样,它也是内部类,并且实现的也是BeanDefinitionRegistryPostProcessor接口,其中postProcessBeanFactory方法为空,所以只需要看其postProcessBeanDefinitionRegistry方法
private static class CachingMetadataReaderFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
private CachingMetadataReaderFactoryPostProcessor() {
}
......
......
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.register(registry);
this.configureConfigurationClassPostProcessor(registry);
}
......
}
首先register方法从内部类SharedMetadataReaderFactoryBean获取了一个BeanDefinition,并注册到了容器的BeanDefinitionMap中
private void register(BeanDefinitionRegistry registry) {
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean.class, SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean::new).getBeanDefinition();
registry.registerBeanDefinition("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory", definition);
}
这个SharedMetadataReaderFactoryBean顾名思义,是一个FactoryBean,同时它实现了BeanClassLoaderAware接口,在这个接口的回调方法setBeanClassLoader中初始化了内部的ConcurrentReferenceCachingMetadataReaderFactory,并在getObject方法返回
static class SharedMetadataReaderFactoryBean implements FactoryBean<ConcurrentReferenceCachingMetadataReaderFactory>, BeanClassLoaderAware, ApplicationListener<ContextRefreshedEvent> {
private ConcurrentReferenceCachingMetadataReaderFactory metadataReaderFactory;
SharedMetadataReaderFactoryBean() {
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.metadataReaderFactory = new ConcurrentReferenceCachingMetadataReaderFactory(classLoader);
}
public ConcurrentReferenceCachingMetadataReaderFactory getObject() throws Exception {
return this.metadataReaderFactory;
}
返回的这个ConcurrentReferenceCachingMetadataReaderFactory,它可以生产一个MetadataReader,这个Reader的作用就是读取类的元数据,包括Class相关的信息,比如是否接口、是否抽象类、是否有注解等等,以及获得注解相关的元数据,比如加了哪些注解等等,在整个Bean的生命周期中起到了非常重要的作用
register方法执行完毕,调用configureConfigurationClassPostProcessor方法
private void configureConfigurationClassPostProcessor(BeanDefinitionRegistry registry) {
try {
BeanDefinition definition = registry.getBeanDefinition("org.springframework.context.annotation.internalConfigurationAnnotationProcessor");
definition.getPropertyValues().add("metadataReaderFactory", new RuntimeBeanReference("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"));
} catch (NoSuchBeanDefinitionException var3) {
;
}
}
先从容器获取了名为internalConfigurationAnnotationProcessor的BeanDefinition,然后把上面生成的metadataReaderFactory设置到它的属性中
在新建Spring容器的时候,会初始化一个BeanDefinitionReader,而这个Reader的初始化过程中,会往容器中注册一个ConfigurationClassPostProcessor,名字就是上面的internalConfigurationAnnotationProcessor,它是Spring容器完成扫描的起点,包括@Component、@Configuration的处理都是在这个类中进行的,而完成这些工作,自然需要解析每个类的元数据,所以它把metadataReaderFactory赋给了ConfigurationClassPostProcessor的属性,后续就会用它来完成一些Bean的元数据解析
ConditionEvaluationReportLoggingListener
public void initialize(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
applicationContext.addApplicationListener(new ConditionEvaluationReportLoggingListener.ConditionEvaluationReportListener());
if (applicationContext instanceof GenericApplicationContext) {
this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
}
}
先往容器的监听器列表添加一个监听器ConditionEvaluationReportListener,这个类是其内部类,通过supportsEventType方法指定了感兴趣的事件类型为ContextRefreshedEvent和ApplicationFailedEvent
private class ConditionEvaluationReportListener implements GenericApplicationListener {
private ConditionEvaluationReportListener() {
}
......
......
public boolean supportsEventType(ResolvableType resolvableType) {
Class<?> type = resolvableType.getRawClass();
if (type == null) {
return false;
} else {
return ContextRefreshedEvent.class.isAssignableFrom(type) || ApplicationFailedEvent.class.isAssignableFrom(type);
}
}
......
}
具体在这两个事件做了什么处理,我们后面说到具体事件的时候再来分析
然后下面的if分支,当前Spring容器的类型是AnnotationConfigServletWebServerApplicationContext,它是GenericApplicationContext的子类,所以会进if分支,调用ConditionEvaluationReport的get方法
public static ConditionEvaluationReport get(ConfigurableListableBeanFactory beanFactory) {
synchronized(beanFactory) {
ConditionEvaluationReport report;
if (beanFactory.containsSingleton("autoConfigurationReport")) {
report = (ConditionEvaluationReport)beanFactory.getBean("autoConfigurationReport", ConditionEvaluationReport.class);
} else {
report = new ConditionEvaluationReport();
beanFactory.registerSingleton("autoConfigurationReport", report);
}
locateParent(beanFactory.getParentBeanFactory(), report);
return report;
}
}
先到容器中找名为autoConfigurationReport的单例Bean,如果没有的话就新建一个,并存储到容器的单例池,然后调用locateParent方法,如果存在父容器,检查父容器中有没有名为autoConfigurationReport的单例Bean,有的话,将父容器中的Report设置到当前Report的parent属性中
private static void locateParent(BeanFactory beanFactory, ConditionEvaluationReport report) {
if (beanFactory != null && report.parent == null && beanFactory.containsBean("autoConfigurationReport")) {
report.parent = (ConditionEvaluationReport)beanFactory.getBean("autoConfigurationReport", ConditionEvaluationReport.class);
}
}
ConditionEvaluationReport的作用是在SpringBoot自动配置的过程中,打印一些匹配结果的DEBUG日志,包括哪些类完成了自动配置,哪些类的哪些条件没有满足而装载失败等等,比如下图中的部分结果
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
CodecsAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer' (OnClassCondition)
CodecsAutoConfiguration.JacksonCodecConfiguration matched:
- @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)
......
......
......
Negative matches:
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
AopAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.aspectj.lang.annotation.Aspect' (OnClassCondition)
......
......
......