【SpringBoot】【五】 创建、准备上下文

发布时间 2023-05-06 16:27:46作者: 酷酷-

1  前言

上节我们看了下环境准备,那么接下来我们就要看重头了,就是创建和准备上下文了。

// 创建上下文
context = createApplicationContext();
// 加载异常解析报告类
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
        new Class[] { ConfigurableApplicationContext.class }, context);
// 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);

2  创建上下文

那我们先来看看创建上下文:

/**
 * DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext"
 * DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"
 * DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"
 */
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            // 应用类型决定创建的上下文的类型 我们这里拿 SERVLET 来说哈,应用类型我们在创建SpringApplication的时候说过了哈
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

我们先看下类图,了解一下上下文整体的继承关系:

ServletWebServerApplicationContext它是一个基于Servlet Web 服务的应用上下文,是Spring Boot 提供的基于Servlet 开发时使用的一个IOC容器,从它的类继承可以看出,它依赖于Spring MVC,而且还提供了两个子类,从名字就知道一个是基于注解,一个是基于XML注册的容器。

创建上下文就是实例化,那我们看看 AnnotationConfigServletWebServerApplicationContext 的构造方法:

public AnnotationConfigServletWebServerApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotatedBeanDefinitionReader从名字上理解,是一个注解方式的BeanDefinition读取器,学过Spring 的都知道首先会加载BeanDefinition,然后根据BeanDefinition生成Bean 对象,这个类的主要作用就是就是加载BeanDefinition了。

ClassPathBeanDefinitionScanner的作用也是加载BeanDefinition,不过从它的名字中,我们知道是一个扫描指定类路径中的BeanDefinition扫描器。

最后,通过构造函数,应用上下文就创建成功了,但是大部分属性都是NULL ,这个创建创建阶段只是完成了初始化对象的创建操作。

3  准备上下文

接下来进入到prepareContext准备上下文方法中:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBa
    // 将环境变量设置到上下文中
    context.setEnvironment(environment);
    // 设置转换器服务
    postProcessApplicationContext(context);
    // 执行初始化
    applyInitializers(context);
    // 执行监听器--这个这里就不看了哈 我们单独有一节说监听器的哈 6个时机的一个
    listeners.contextPrepared(context);
    // 打印启动日志
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    // 获取上下文中的 BeanFactory,添加一些特殊的单例 Bean 比如springBootBanner、springApplicationArguments
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 注册 springApplicationArguments Bean
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        // 注册 springBootBanner
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        // 是否允许允许 Bean 定义覆盖,默认false
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // 获取启动类
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 加载
    load(context, sources.toArray(new Object[0]));
    // 执行监听器 --这个这里也不看了哈 我们单独有一节说监听器的哈 6个时机的一个
    listeners.contextLoaded(context);
}

3.1  设置环境变量

setEnvironment方法中会将环境对象设置给上下文及读取器和扫描器:

/** Environment used by this context. */
@Nullable
private ConfigurableEnvironment environment;

@Override
public void setEnvironment(ConfigurableEnvironment environment) {
    this.environment = environment;
}
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
    super.setEnvironment(environment);
    this.reader.setEnvironment(environment);
    this.scanner.setEnvironment(environment);
}

3.2  postProcessApplicationContext 上下文增强

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    // Bean 名称生成器 默认的为 org.springframework.context.annotation.internalConfigurationBeanNameGenerator
    if (this.beanNameGenerator != null) {
        context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                this.beanNameGenerator);
    }
    // 资源加载器
    if (this.resourceLoader != null) {
        if (context instanceof GenericApplicationContext) {
            ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
    // 给上下文的工厂设置环境准备时初始化的一堆默认转换器转换服务
    if (this.addConversionService) {
        context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    }
}

3.3  applyInitializers 执行初始化器

applyInitializers方法中会调用之前SPI 机制加载的ApplicationContextInitializer的所有实例的初始化方法:

@SuppressWarnings({ "rawtypes", "unchecked" })
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);
    }
}

我们回顾下初始化器:

(1)ConfigurationWarningsApplicationContextInitializer,主要是添加了警告后置处理器,检查注解扫描的包是否有问题,如果有,则返回警告:

public class ConfigurationWarningsApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    private static final Log logger = LogFactory.getLog(ConfigurationWarningsApplicationContextInitializer.class);
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        // 将ConfigurationWarningsPostProcessor后置处理器注入到应用上下文中
        context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
    }
}

(2)ContextIdApplicationContextInitializer,作用为创建一个ContextId对象,并将其注册到Bean 工厂中

public class ContextIdApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    private int order = Ordered.LOWEST_PRECEDENCE - 10;
    @Override
    public int getOrder() {
        return this.order;
    }
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ContextId contextId = getContextId(applicationContext);
        applicationContext.setId(contextId.getId());
        applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
    }
}

(3)DelegatingApplicationContextInitializer是一个委托的初始化器,会加载context.initializer.classes配置的初始化器,然后进行调用:

public class DelegatingApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    private static final String PROPERTY_NAME = "context.initializer.classes";
    private int order = 0;
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        ConfigurableEnvironment environment = context.getEnvironment();
        List<Class<?>> initializerClasses = getInitializerClasses(environment);
        if (!initializerClasses.isEmpty()) {
            applyInitializerClasses(context, initializerClasses);
        }
    }
}

(4)ServerPortInfoApplicationContextInitializer,它本身也是一个监听器,监听的事件为WebServerInitializedEvent,可以将端口设置到环境中,然后方便通过@Valueenvironment获取本地端口号:

public class ServerPortInfoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<WebServerInitializedEvent> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 自己本身是实现监听器接口的
        applicationContext.addApplicationListener(this);
    }

    @Override
    public void onApplicationEvent(WebServerInitializedEvent event) {
        String propertyName = "local." + getName(event.getApplicationContext()) + ".port";
        // 设置端口
        setPortProperty(event.getApplicationContext(), propertyName, event.getWebServer().getPort());
    }
}

(5)SharedMetadataReaderFactoryContextInitializer,它的主要作用是创建了一个Bean 工厂后置处理器并设置到上下文中:

class SharedMetadataReaderFactoryContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor());
    }
}

(6)ConditionEvaluationReportLoggingListener,也是加了一个监听器,可以打印上下文容器成功刷新或失败的日志报告:

public class ConditionEvaluationReportLoggingListener implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        // 添加监听器
        applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
        if (applicationContext instanceof GenericApplicationContext) {
            // Get the report early in case the context fails to load
            this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
        }
    }
}

接着进入到listeners.contextPrepared(context)方法,调用SpringApplicationRunListeners监听器,我们之前看过了哈这里就不再看了哈,然后开始进入下一步骤阶段。

3.4  load 

接着prepareContext方法又添加了一些后置处理器和加载了一些Bean ,最后进入load方法,将启动类 BeanDefinition注册。

protected void load(ApplicationContext context, Object[] sources) {
    // 打印日志
    if (logger.isDebugEnabled()) {
        logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    // 创建 BeanDefinition 加载器
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    // 加载
    loader.load();
}
public int load() {
    int count = 0;
    for (Object source : this.sources) {
        count += load(source);
    }
    return count;
}
private int load(Class<?> source) {
    // 传入启动类 Class
    // 判断是否使用groovy脚本 --这个暂时不知道具体是要干什么
    if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        // Any GroovyLoaders added in beans{} DSL can contribute beans here
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
        load(loader);
    }
    // 启动类本身是个@Component
    if (isComponent(source)) {
        // 调用AnnotatedBeanDefinitionReader 注册启动类去注册当前启动类
        this.annotatedReader.register(source);
        return 1;
    }
    return 0;
}

最后调用listeners.contextLoaded进行监听器的处理,这里发布的是ApplicationPreparedEvent事件,这个之前也看过了哈,这里也不细说了哈。

4  小结

好了,这节我们主要看了下上下文的创建和准备工作哈,有理解不对的地方欢迎执政哈。