Spring应用上下文的准备

为讷河等地区用户提供了全套网页设计制作服务,及讷河网站建设行业解决方案。主营业务为成都网站设计、成都网站制作、讷河网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
我们在上一节完成了应用上下文的创建工作,SpringApplication 继续通过 prepareContext方法来进行应用上下文的准备工作。首先,通过图 4-4 来整体了解一下 prepareContext 的核心功能及流程。
配合流程图,看一下 SpringApplication 中 prepareContext 方法源代码及功能注解。
- private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment
 - environment,
 - SpringApplicationRunL
 - isteners
 - listeners ,
 - Applicat ionArguments applicat ionArguments, Bann
 - er printedBanner) {
 - //没置上下文的配置环境
 - context . setEnvironment (environment);
 - //应用上下文后置处理
 - postProcessApplicationContext( context);//在 context 刷新之前,Appl icat ionContext Init
 - ial izer 初始化 context
 - applyInitializers(context);
 - //通知监听器 context 准备完成,该方法以 上为上下文准备阶段,以下为上下文加载阶段
 - listeners . contextPrepared(context);//打印日志,启动 Profile
 - if (this . logStartupInfo)-
 - logStartupInfo(context . getParent() == nu1l);
 - logStartupProfileInfo( context);
 - }
 - //获得 ConfigurableL istableBeanFactory 并炷册单例对象
 - ConfigurableL istableBeanFactory beanFactory = context . getBeanFactory();
 - beanFactory. registerSingleton("springApplicat ionArguments", applicationAr
 - guments);
 - if (printedBanner != null) {
 - //注册打印日志对象
 - beanF actory. registerSingleton("springBootBanner", printedBanner);
 - if (beanFactory instanceof DefaultlistableBeanFactory) {
 - //没置是否允许覆盖炷册
 - ((DefaultListableBeanFactory) beanFactory)
 - . setAllowBeanDefinitionOverriding(this . allowBeanDefinitionOverriding);
 - //获取全部配置源,其中包含 primarySources 和 sources
 - Set<0bject> sources = getAllSources();
 - Assert . notEmpty(sources, "Sources must not be empty");
 - //将 sources 中的 Bean 加载到 context 中
 - load(context, sources . toArray(new 0bject[0]));
 - //遁知监听器 context 加载完成
 - listeners . contextLoaded(context);
 - }
 
通过流程图和具体代码可以看出,在该方法内完成了两步操作:应用上下文的准备和加载。
下面我们针对具体的源代码进行详细讲解。
应用上下文准备阶段
在上下文准备阶段,主要有 3 步操作:对 context 设 置 environment、应用上下文后置处理和 ApplicationContextlnitializer 初始化 context 操作。
首先是对 context 设置 environment,代码和业务操作都很简单。
- public void setEnvironment (ConfigurableEnvironment environment) {
 - //设置 context 的 environment
 - super. setEnvi ronment( environment);
 - //设置 context 的 reader 属性的 conditionEvaluator 属性 this.reade
 - er. settEnvironment(environment) ;
 - //设置 context 的 scanner 属性的 environment 属性
 - this. scanner. setEnvi ronment ( envi ronment);
 - }
 
随 后 , 便 是 进 行 Spring 应 用 上 下 文 的 后置处理 , 这 一 步 是 通 过postProcessApplicationContext 方法来完成的。
- protected void postProcessApplicat ionContext (ConfigurableApplicat ionConEext
 - context){
 - f (this. beanNameGenerator != null) {
 - // 如果 beanNameGenerator 为 null, 则将当前的 beanNameGenerator 按照默认名字进
 - 行注册
 - context . getBeanFactory(). regi sterSingleton(
 - Annotat ionConfigUtils .CONF IGURATION BEAN NAME GENERATOR,
 - this . beanNameGenerator);
 - esourceLoader 为 null 时, 则根据 context 的类型分别进行 Resourceloader 和 CL
 - assLoader 的设置
 - if (this .resourceLoader != null) {
 - F (context instanceof GenericApplicationContext) {
 - ((GenericApplicationContext) context) . setResourcel oader(this . resource
 - Loader);
 - if (context instanceof DefaultResourceLoader) {
 - ( (DefaultResourceLoader) context)
 - . setClassLoader(this.resourceLoader. getClassLoader());
 - //如果为 true 则获取并没置转换服务
 - f (this .addConversionService) {
 - context . getBeanFactory(). setConversionService(
 - ApplicationConversionService . getSharedInstance());
 - }
 
postProcessApplicationContext 方 法 主 要 完 成 上 下 文 的 后 置 操 作 , 默 认 包 含beanNameGeneratorResourceL oader.ClassL oader 和 ConversionService 的设置。该方法可由子类覆盖实现,以添加更多的操作。
而在此阶段,beanNameGenerator 和 resourceL oader 都为 null,因此只操作了最后-一步的设置转换服务。
最后,在通知监听器 context 准备完成之前,通过 applylnitializers 方法对上下文进行初始化。
所使用的 ApplicationContextInitializer 正是我们在 SpringApplication 初始化阶段设置在itializers 变量中的值,只不过在通过 getlnitializers 方法获取时进行了去重和排序。
- protected void applyInitializers(ConfigurableApplicat ionContext context) {
 - /获取 Appl icat ionContextInitializer 集合并遍历
 - for (ApplicationContextInitializer initializer : getInitializers()) {
 - //解析当前 initial izer.实现的 Appl icat ionContextInitializer 的泛型参数
 - Class> requiredType = GenericTypeResolver . resolveTypeArgument(
 - initializer . getClass(), ApplicationContextInitializer.class);
 - 1 断言判断所需类似是否与 context 类型匹配
 - Assert. isInstanceOf(requiredType, context, "Unable to call initialize
 - r.");
 - // 初始化 context
 - initializer. initialize(context);
 - }
 - }
 
完成以上操作之后,程序便调用 SpringApplicationRunListeners 的 contextPrepared 方法通知监听器,至此第一阶段的准备操作完成。
应用上下文加载阶段
应用上下文加载阶段包含以下步骤:打印日志和 Profile 的设置、设置是否允许覆盖注册、获取全部配置源、将配置源加载入上下文、通知监控器 contex 加载完成。
首先进入应用上下文加载阶段的操作为打印日志和 Profile 的设置,对此不展开讲解。随后,便是获得 ConfigurableL istableBeanFactory 并注册单例对象,注册的单例对象包含:
ApplicationArguments 和 Banner。 当 BeanFactory 为 DefaultL istableBeanFactory 时,进入设置是否允许覆盖注册的处理逻辑。
此处需注意的是,当进行了 ApplicationArguments 类单例对象的注册之后,也就意味着我们在使用 Spring 应用上下文的过程中可以通过依赖注入来使用该对象。
- @Resource
 - private ApplicationArguments applicat ionArguments;
 
完成以.上操作后,便进入配置源信息的处理阶段,这一步通过 getAllSources 方法来对配置源信息进行合并操作。
- public Set
 - Set<0bject> allSources = new LinkedHashSet<>();
 - if (!CollectionUtils.isEmpty(this . primarySources)) {
 - allSources.addAll(this.primarySources);
 - if (!CollectionUtils . isEmpty(this. sources)) {
 - allSources. addAll(this.sources);
 - }
 - }
 
return Collections . unmodifiableSet(allSources); }以上操作逻辑很简单,如果 Set 集合中不存在 primarySources 配置源或 sources 配置源,则将其添加入 Set 中,同时将 Set 设置为不可修改,并返回。
前面章节已经提到,变量 primarySources 的值 来自 SpringApplication 的构造参数,变量sources 的值来自 setResources 方法。
当获得所有的配置源信息之后,通过 load 方法将配置源信息加载到上下文中,代码如下。
- protected void load(ApplicationContext context, Object[] sources) {
 - /日志打印
 - BeanDefinitionLoader loader = createBeanDefinitionLoader(
 - getBeanDefinitionRegistry(context), sources);
 - f (this. beanNameGenerator != nu1l).
 - loader. setBeanNameGenerator(this . beanNameGenerator);
 - if (this.resourceLoader != nu1l) {
 - loader . setResourceLoader(this . resourceLoader);
 - if (this. environment != null) {
 - loader . setEnvironment (this . environment) ;
 - loader. load();
 - }
 
该方法主要通过 BeanDefinitionL oader 来完成配置资源的加载操作。我们进一步查看方法createBeanDefinitionL oader 的源代码,会发现它最终调用了 BeanDefinitionL oader 的构造方法,并进行初始化操作。
- BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
 - this. sources = sources;
 - this . annotatedReader = new AnnotatedBeanDefinitionReader(registry);
 - this . xmlReader = new XmlBeanDefinitionReader(registry);
 - if (isGroovyPresent())
 - this. groovyReader = new GroovyBeanDefinitionReader(registry);
 - }
 
通过 BeanDefinitionLoader 的构造方法我们可以看到 BeanDefinitionLoader 支持基于AnnotatedBeanDefinitionReaderXmlBeanDefinitionReader、GroovyBeanDefinitionReader等 多种类型的加载操作。
在执行完 BeanDefinitionL oader 的创建及基本属性设置之后,调用其 load方法,该方法最终执行以下代码。
- private int load(0bject source) {
 - Assert. notNull(source, "Source must not be null");
 - if (source instanceof Class>) {
 - return load((Class>) source);
 - }if (source instanceof Resource)
 - return load( (Resource) source);
 - }
 - if (source instanceof Package) {
 - return load( (Package) source);
 - }
 - if (source instanceof CharSequence) {
 - return load( (CharSequence) source);
 - throw new IllegalArgumentException("Invalid source type ”+ source. getC
 - lass());}
 
从以上代码可以看出,BeanDefinitionLoader 加载支持的范围包括:
Class、Resource、 Package 和 CharSequence 四种。 前面我们已经提到变量 sources的来源有 primarySources 配置源和 sources 配置源。变量 primarySources 在初始化时接收的类型为 Class,而变量 sources 通过 set(Set )方法接收的参数为 String 集合。
因此,在实际使用的过程中,Resource 和 Package 的判断分支始终无法进入执行阶段。
完成以上操作后,接下来执行 SpringApplicationRunListeners 的 contextL oaded 方法通知监听器上下文加载完成,至此整个 Spring 应用上下文的准备阶段完成。
Copyright © 2009-2022 www.wtcwzsj.com 青羊区广皓图文设计工作室(个体工商户) 版权所有 蜀ICP备19037934号