本篇文章是对上篇prepareContext的补充,在该方法的执行过程中,遍历了最初从META-INF/spring.factories文件中加载到的ApplicationContextInitializer,依次调用了其initialize方法。
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
SpringApplication初始化的时候,一共加载到了7个内置的ApplicationContextInitializer,本篇文章就逐一分析每个内置的初始化器做了哪些事情
spring-boot/src/main/resources/META-INF/spring.factories
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
获取环境中属性context.initializer.classesInitializer配置的ApplicationContextInitializer列表, 执行其initialize()方法。
SpringBoot允许我们通过各种属性配置方式自定义一些ApplicationContextInitializer,它们的调用时机就是该类的initialize方法
//代理初始化器
public class DelegatingApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
private static final String PROPERTY_NAME = "context.initializer.classes";
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
//获取environment中配置的context.initializer.classes属性
//其值为各个initializer,用逗号分隔开
List<Class<?>> initializerClasses = getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
//获取到各个initializer,并执行其initialize()方法
applyInitializerClasses(context, initializerClasses);
}
}
}
进入getInitializerClasses方法
private static final String PROPERTY_NAME = "context.initializer.classes";
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
String classNames = env.getProperty(PROPERTY_NAME);
List<Class<?>> classes = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
classes.add(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<>();
for (Class<?> initializerClass : initializerClasses) {
initializers.add(instantiateInitializer(contextClass, initializerClass));
}
applyInitializers(context, initializers);
}
先实例化,然后调用applyInitializers方法
private void applyInitializers(ConfigurableApplicationContext context,
List<ApplicationContextInitializer<?>> initializers) {
initializers.sort(new AnnotationAwareOrderComparator());
for (ApplicationContextInitializer initializer : initializers) {
initializer.initialize(context);
}
}
最终调用了它们的initialize方法
添加了一个类型为CachingMetadataReaderFactoryPostProcessor的BeanFactoryPostProcessor, CachingMetadataReaderFactoryPostProcessor会做两件事情
class SharedMetadataReaderFactoryContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
public static final String BEAN_NAME = "org.springframework.boot.autoconfigure."
+ "internalCachingMetadataReaderFactory";
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
//spring上下文容器添加一个CachingMetadataReaderFactoryPostProcessor
applicationContext.addBeanFactoryPostProcessor(
new 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的元数据解析
初始化容器ID,这个类的作用是给容器设置一个ID,其实就是我们的项目名, 获取属性spring.application.name配置的应用名称, 如果不存在的话, 默认使用application
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
//获取并设置容器ID
ContextId contextId = getContextId(applicationContext);
applicationContext.setId(contextId.getId());
//容器beanFactory中注册一个名称为ContextId类名
//值为contextId的bean
applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(),
contextId);
}
第一行先获取了一个容器ID对象,然后把ID属性设置到容器中,并把这个ID对象作为一个单例Bean注册到容器的单例池
我们看下这个ContextId怎么来的,进入getContextId方法
//获取ContextID
private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
//父容器获取spring.application.name对应的bean
ApplicationContext parent = applicationContext.getParent();
if (parent != null && parent.containsBean(ContextId.class.getName())) {
return parent.getBean(ContextId.class).createChildId();
}
//父容器获取不到,则生成一个contextId
return new ContextId(getApplicationId(applicationContext.getEnvironment()));
}
如果有父容器,就根据父容器的ID创建一个子容器ID,格式为 父容器ID - 子容器序号
ContextIdApplicationContextInitializer.ContextId createChildId() {
return ContextIdApplicationContextInitializer.this.new ContextId(this.id + "-" + this.children.incrementAndGet());
}
如果没有父容器,就到environment中取spring.application.name属性,没有配置的话默认为application
//获取应用ID
private String getApplicationId(ConfigurableEnvironment environment) {
//获取属性:spring.application.name
String name = environment.getProperty("spring.application.name");
//如果为空,默认名application
return StringUtils.hasText(name) ? name : "application";
}
将取到的结果作为参数传给ContextId的构造函数
//ContextId类
class ContextId {
//原子Long类
private final AtomicLong children = new AtomicLong(0);
private final String id;
ContextId(String id) {
this.id = id;
}
ContextId createChildId() {
//线程安全递增
return new ContextId(this.id + "-" + this.children.incrementAndGet());
}
String getId() {
return this.id;
}
}
也就是说默认情况下,这个ContextId存了我们的项目名,然后把它设置到了容器中
配置告警初始化器(通过分析源码实际情况,默认扫描启动类所在的路径(或者@ComponentScan注解指定的路径)如果系统配置包扫描到了org或者org.springframework包就会发出警告打印warn日志并停止系统启动
)
ConfigurationWarningsApplicationContextInitializer初始化器源码如下:
public class ConfigurationWarningsApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Log logger = LogFactory.getLog(ConfigurationWarningsApplicationContextInitializer.class);
//初始化方法会在容器启动时调用,并将ConfigurationWarningsPostProcessor后置处理器注入到应用上下文中
@Override
public void initialize(ConfigurableApplicationContext context) {
context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
/**
* 返回有问题的扫描包(@ComponentScan)Check对象
* @return the checks to apply
*/
protected Check[] getChecks() {
return new Check[] { new ComponentScanPackageCheck() };
}
}
ComponentScanPackageCheck是ConfigurationWarningsApplicationContextInitializer的一个内部类,源码分析
/**
* 可以应用的单一检查
*/
@FunctionalInterface
protected interface Check {
/**
* 返回检查结果,如果检查失败,则返回警告,如果没有问题,则返回null
* @param registry the {@link BeanDefinitionRegistry}
* @return a warning message or {@code null}
*/
String getWarning(BeanDefinitionRegistry registry);
}
/**
* 检查@ComponentScan注解扫描有问题的包
*/
protected static class ComponentScanPackageCheck implements Check {
private static final Set<String> PROBLEM_PACKAGES;
//定义扫描有问题的包
static {
Set<String> packages = new HashSet<>();
packages.add("org.springframework");
packages.add("org");
PROBLEM_PACKAGES = Collections.unmodifiableSet(packages);
}
//检查@ComponentScan注解扫描的包是否有问题,如果有,则返回警告,否则返回null
@Override
public String getWarning(BeanDefinitionRegistry registry) {
//获取@ComponentScan注解扫描的包集合
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, ", ") + ".";
}
//获取@ComponentScan注解扫描的包
protected Set<String> getComponentScanningPackages(BeanDefinitionRegistry registry) {
Set<String> packages = new LinkedHashSet<>();
//获取容器中所有bean定义名称
String[] names = registry.getBeanDefinitionNames();
for (String name : names) {
//获取name对应的bean定义对象
BeanDefinition definition = registry.getBeanDefinition(name);
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annotatedDefinition = (AnnotatedBeanDefinition) definition;
addComponentScanningPackages(packages, annotatedDefinition.getMetadata());
}
}
return packages;
}
//将bean实例上注解@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()));
}
}
}
private void addPackages(Set<String> packages, String[] values) {
if (values != null) {
Collections.addAll(packages, values);
}
}
private void addClasses(Set<String> packages, String[] values) {
if (values != null) {
for (String value : values) {
packages.add(ClassUtils.getPackageName(value));
}
}
}
//获取有问题的扫描包集合,即包名是:org或org.springframework
private List<String> getProblematicPackages(Set<String> scannedPackages) {
List<String> problematicPackages = new ArrayList<>();
for (String scannedPackage : scannedPackages) {
//判定包名是否有问题,即包名是:org或org.springframework
if (isProblematicPackage(scannedPackage)) {
problematicPackages.add(getDisplayName(scannedPackage));
}
}
return problematicPackages;
}
//判定包名是否有问题,即包名是:org或org.springframework
private boolean isProblematicPackage(String scannedPackage) {
if (scannedPackage == null || scannedPackage.isEmpty()) {
return true;
}
return PROBLEM_PACKAGES.contains(scannedPackage);
}
private String getDisplayName(String scannedPackage) {
if (scannedPackage == null || scannedPackage.isEmpty()) {
return "the default package";
}
return "'" + scannedPackage + "'";
}
}
最终在控制台打印如下日志
** WARNING ** : Your ApplicationContext is unlikely to start due to a @ComponentScan of 'org'.
这里只是打印一行日志,并不会停止程序,不过实际测试下来程序是没办法正常启动的,这个路径是Spring框架本身的包路径,扫描这个包会干扰Spring正常执行流程,陷入循环,当然正常情况下我们项目的路径也不会这样定义。
服务端口初始化器, 分别实现了ApplicationContextInitializer和ApplicationListener接口, 在applicationContext中添加了事件监听器this, 监听了WebServerInitializedEvent事件, 配置服务的端口号
public class ServerPortInfoApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext>,
ApplicationListener<WebServerInitializedEvent> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
//把this添加到Application的listener中
applicationContext.addApplicationListener(this);
}
//监听处理WebServerInitializedEvent事件
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
//获取environment中配置的server.ports
String propertyName = "local." + getName(event.getApplicationContext()) + ".port";
setPortProperty(event.getApplicationContext(), propertyName,
event.getWebServer().getPort());
}
}
条件评估日志监听器, 主要作用是给applicationContext添加了一个ConditionEvaluationReportListener监听器, ConditionEvaluationReportListener监听了ContextRefreshedEvent和ApplicationFailedEvent事件, 打印相应日志
public class ConditionEvaluationReportLoggingListener
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
//applicationContext中添加一个ConditionEvaluationReportListener
applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
if (applicationContext instanceof GenericApplicationContext) {
//注册一个ConditionEvaluationReport bean
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方法
//用于记录Condition注解的评估情况
public final class ConditionEvaluationReport {
//bean名称为autoConfigurationReport
//类型为ConditionEvaluationReport
private static final String BEAN_NAME = "autoConfigurationReport";
//从beanFactory中获取ConditionEvaluationReport实例
public static ConditionEvaluationReport get(
ConfigurableListableBeanFactory beanFactory) {
synchronized (beanFactory) {
ConditionEvaluationReport report;
if (beanFactory.containsSingleton(BEAN_NAME)) {
//如果bean已经被注册,立即返回
report = beanFactory.getBean(BEAN_NAME, ConditionEvaluationReport.class);
}
else {
//否则注册bean
report = new ConditionEvaluationReport();
beanFactory.registerSingleton(BEAN_NAME, 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)
......
......
......
添加了一个 RSocketServerInitializedEvent事件的 监听器到 ApplicationContext中。
public class RSocketPortInfoApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
/**
* 注入一个端口检查和设置的监听器,对应的事件RSocketServerInitializedEvent
**/
applicationContext.addApplicationListener(new Listener(applicationContext));
}
//这里直接写了个内部类实现RSocketServerInitializedEvent事件的监听
private static class Listener implements ApplicationListener<RSocketServerInitializedEvent> {
private static final String PROPERTY_NAME = "local.rsocket.server.port";
private final ConfigurableApplicationContext applicationContext;
Listener(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(RSocketServerInitializedEvent event) {
if (event.getServer().address() != null) {
setPortProperty(this.applicationContext, event.getServer().address().getPort());
}
}
private void setPortProperty(ApplicationContext context, int port) {
if (context instanceof ConfigurableApplicationContext) {
setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), port);
}
if (context.getParent() != null) {
setPortProperty(context.getParent(), port);
}
}
private void setPortProperty(ConfigurableEnvironment environment, int port) {
MutablePropertySources sources = environment.getPropertySources();
PropertySource<?> source = sources.get("server.ports");
if (source == null) {
source = new MapPropertySource("server.ports", new HashMap<>());
sources.addFirst(source);
}
setPortProperty(port, source);
}
@SuppressWarnings("unchecked")
private void setPortProperty(int port, PropertySource<?> source) {
((Map<String, Object>) source.getSource()).put(PROPERTY_NAME, port);
}
}
}
所有的这些初始化类都没有进行启动服务的实质性操作,都是通过注册对象,埋点,后面invokeBeanFactoryPostProcessors才真正调用初始化方法,而且在项目启动之前