【SpringBoot】【五】 创建、准备上下文
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
,可以将端口设置到环境中,然后方便通过@Value
或environment
获取本地端口号:
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 小结
好了,这节我们主要看了下上下文的创建和准备工作哈,有理解不对的地方欢迎执政哈。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了