Spring MVC 四:Context层级

这一节我们来回答上篇文章中避而不谈的有关什么是RootApplicationContext的问题。

这就需要引入Spring MVC的有关Context Hierarchy的问题。Context Hierarchy意思就是Context层级,既然说到Context层级,说明在Spring MVC项目中,可能存在不止一个Context。

Context是上下文的意思,我们可以直接理解为容器,上一篇文章我们提到了ServletApplicationContext的概念,可以理解为Servlet容器。

除此之外,Spring项目中肯定还有有IoC容器,我们今天就来聊一下这两个容器之间的关系。

为什么需要容器

我们可以这样理解:凡是具有生命周期的对象,也就是说对象并不是在当前现成创建、使用完成之后就销毁的,而是在当前现成使用完成之后不销毁、其他线程还需要继续使用的。这中对象就需要一个容器来保存。

比如Spring的单例Bean,在Spring初始化的过程中就会创建并存入Ioc容器,之后应用使用过程中从Ioc容器中获取。

Servlet对象(比如DispatchServlet)也是这样,在Web应用初始化的过程中创建,Controller、ViewResolver、ExceptionHandler等对象随之也完成创建,这些对象在整个应用的生命周期中会反复使用,因此,Servlet也必须要有一个容器来存储。

Spring把容器称之为上下文,Context。

我们可以把Servlet容器叫做WebApplicationContext,因为Servlet的出现就是为了解决Web应用的,所以自然而然的,我们可以把Servlet容器称为WebApplicationContext。

相对应的,Spring Ioc容器,我们可以称之为RootApplicationContext:根容器。

Servlet和根容器的关系

下图一目了然的说明了两者之间的关系:
在这里插入图片描述

Servlet容器存放Controller、VIewResolver、HanderMapping等DispatcherServlet的相关对象,根容器可以存放其他Service、Repositories等对象。

一个DispatcherServlet可以对应的有一个Servlet容器,一个Web应用可以有多个DispatcherServlet(这种应用其实比较少见),所以一个应用可以有多个Servlet容器。但是一般情况下,即使有多个Servlet容器,一个应用也希望只有一个根容器,以便在不同的Servlet容器之间共享根容器的对象。

举例

我们下面用几个例子来说明两者之间的关系。

还是延用上一篇文章的例子,并做如下简单的改造。

首先,增加一个单例bean,以便启用Spring IoC容器。我们只是简单引入IoC容器,单例bean不需要太复杂,

package org.example.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;@Component
public class UserService {@AutowiredApplicationContext app;public String getUserInfo(){System.out.println(" application in UserService:"+ app);return "This is userinfo...from UserService...";}
}

只有一个方法,返回String。不过为了能说明当前单例Bean所处的容器,我们通过@Autowired引入ApplicationContext对象(希望大家还记得这一点,我们前面讲过通过什么样的方式,能够在应用中拿到Bean所处的Application对象),这样的话我们就能够知道当前Spring应用的Ioc容器具体是哪个对象。

其次,我们需要新增一个Spring Ioc容器的配置类,我们称之为RootConfiguration,配置类仅指定扫描路径即可:

import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan("org.example.service")
public class RootConfiguration {
}

最后,Controller改造一下,与UserService一样,引入ApplicationContext(Controller中的ApplicationContext,我们可以人为他就是Servlet容器),log打印一下具体的Context,同时我们打印一下当前Servlet容器的父容器:

package org.example.controller;import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;@Controller
public class HelloWorldController {@AutowiredUserService userService;@AutowiredApplicationContext app;@GetMapping("/hello")@ResponseBodypublic String hello(ModelAndView model){DispatcherServlet d;String userInfo = userService.getUserInfo();System.out.println("app in controller:"+app);System.out.println("servletContext's parent Context"+app.getParent());return "<h1>"+userInfo+"</h1>";}
}

OK,准备工作完成,开始验证。

举例1 根容器不是必须存在

首先,根容器不是必须存在的。

但是由于我们增加了UserService这个bean,所以必须有Ioc容器,我们必须为IoC容器指定配置文件的包扫描路径。

既然我们说根容器不是必须存在,那么,意思就是说,Servlet容器要同时充当IoC容器的角色。

所以我们必须在MvcConfiguration中增加宝扫描路径:

package org.example.configuration;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan({"org.example.service","org.example.controller"})
//@ComponentScan({"org.example.controller"})
public class MvcConfiguration {
}

MvcInitializer中的getRootConfigClasses()返回null,则应用初始化的过程中就不会创建根容器(通过查看createRootApplicationContext方法源码得出的结论):

public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return null;}

启动应用,测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBftrdeQ-1693234193599)(/img/bVc9m07)]
说明应用是可以正常运行的,后台log:

 application in UserService:WebApplicationContext for namespace 'dispatcher-servlet', started on Tue Aug 22 22:35:34 CST 2023
app in controller:WebApplicationContext for namespace 'dispatcher-servlet', started on Tue Aug 22 22:35:34 CST 2023
servletContext's parent Contextnull

后台log说明,Servlet容器和Spring IoC容器是同一个,他们的父容器是null。

指定Ioc容器为父容器

首先修改MvcConfiguration,指定Servlet容器只扫描Controller包:

package org.example.configuration;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan({"org.example.controller"})
public class MvcConfiguration {
}

MvcInitializer中的getRootConfigClasses()返回我们新增的RootConfiguration,则应用初始化的过程中就会创建根容器:

public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[] {RootConfiguration.class};}

启动应用,测试:

 application in UserService:Root WebApplicationContext, started on Tue Aug 22 22:44:08 CST 2023
app in controller:WebApplicationContext for namespace 'dispatcher-servlet', started on Tue Aug 22 22:44:09 CST 2023, parent: Root WebApplicationContext
servletContext's parent ContextRoot WebApplicationContext, started on Tue Aug 22 22:44:08 CST 2023

有测试结果可知:
1. UserService所处的是根容器。
2. Controller所处的是Servlet容器。
3. Servlet容器的父容器是根容器。

测试结果验证了我们前面的推论,而且,Spring底层自动将Servlet容器的父容器设置为了根容器!在什么地方设置的?

Spring MVC框架在DispatcherServlet的初始化过程中(init方法),initWebApplicationContext的时候设置ServletContext的父容器为根容器。

上一篇 Spring MVC 三 :基于注解配置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/87110.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序 趣味学习与益智游戏系统APP

管理员、用户可通过HBuilder系统手机打开系统&#xff0c;注册登录后可进行管理员后端&#xff1b;首页、个人中心、用户管理、学生分类管理、学一学管理、玩一玩管理、听一听管理、试题管理、练一练管理、系统管理、考试管理&#xff0c;用户前端&#xff1b;首页、学一学、玩…

不同子网络中的通信过程

从输入www.baidu.com经历了什么 一、DNS&#xff08;网址->IP&#xff09; 二、ARP&#xff08;IP->MAC&#xff09; A->B&#xff1a;有数据发送&#xff0c;数据封装ip之后发现没有主机B的mac地址。然后ARP在本网段广播&#xff1a;检查目标地址和源地址是否在同一…

【C语言进阶(8)】自定义数据类型1:结构体

文章目录 前言Ⅰ 结构体的声明和定义⒈结构体声明⒉结构体定义⒊特殊的声明 Ⅱ 结构体的自引用Ⅲ 结构体初始化Ⅳ 访问结构体成员Ⅴ 结构体内存对齐⒈结构体内存对齐规则⒉分析结构体大小⒊嵌套结构体内存大小⒋内存对齐存在的原因 Ⅵ 修改默认对齐数Ⅶ 结构体传参 前言 C 语言…

9.4 集成功率放大电路

OTL、OCL 和 BTL 电路均有各种不同输出功率和不同电压增益的集成电路。应当注意&#xff0c;在使用 OTL 电路时&#xff0c;需外接输出电容。为了改善频率特性&#xff0c;减小非线性失真&#xff0c;很多电路内部还引入深度负反馈。这里以低频功放为例。 一、集成功率放大电路…

C++——引用

引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在的变量取一个别名&#xff0c;编译器不会因为引用变量而开辟内存空间&#xff0c;它和它引用的变量公用同一块空间。 相当于是给被引用的变量取了一个小名&#xff0c;但是相当于是同一个变量。 类型& 引用变…

C#,《小白学程序》第一课:初识程序

曰&#xff1a;扫地僧练就绝世武功的目的是为了扫地更干净。 1 引言 编程只是一项技术&#xff0c;如包包子&#xff0c;不是什么高深的科学。 学习程序最不好的方法是先学习枯燥的语法。 学习程序主要是用代码解决问题。因此&#xff0c;我们抛开所有的语法与诸多废物&…

sql:SQL优化知识点记录(四)

&#xff08;1&#xff09;explain之ref介绍 type下的ref是非唯一性索引扫描具体的一个值 ref属性 例如&#xff1a;ti表先加载&#xff0c;const是常量 t1.other_column是个t1表常量 test.t1.ID&#xff1a;test库t1表的ID字段 t1表引用了shared库的t2表的col1字段&#x…

HTML之VSCode简单配置与创建

目录 插件下载 然后输入源码&#xff1a; 使用 效果 插件下载 下载这个插件后可以直接运行&#xff1a; 然后创建一个文件&#xff1a; 然后输入源码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"…

几个nlp的小任务(机器翻译)

几个nlp的小任务(机器翻译) 安装依赖库数据集介绍与模型介绍加载数据集看一看数据集的样子评测测试数据预处理测试tokenizer处理目标特殊的token预处理函数对数据集的所有数据进行预处理微调预训练模型设置训练参数需要一个数据收集器,把处理好数据喂给模型设置评估方法参数…

Python“牵手”淘宝商品列表数据,关键词搜索淘宝API接口数据,淘宝API接口申请指南

淘宝平台API接口是为开发电商类应用程序而设计的一套完整的、跨浏览器、跨平台的接口规范&#xff0c;淘宝API接口是指通过编程的方式&#xff0c;让开发者能够通过HTTP协议直接访问淘宝平台的数据&#xff0c;包括商品信息、店铺信息、物流信息等&#xff0c;从而实现淘宝平台…

Linux网络编程:线程池并发服务器 _UDP客户端和服务器_本地和网络套接字

文章目录&#xff1a; 一&#xff1a;线程池模块分析 threadpool.c 二&#xff1a;UDP通信 1.TCP通信和UDP通信各自的优缺点 2.UDP实现的C/S模型 server.c client.c 三&#xff1a;套接字 1.本地套接字 2.本地套 和 网络套对比 server.c client.c 一&#xff1a;线…

taro react/vue h5 中的上传input onchange 值得区别

<inputclassNamebase-input-file-h5typefileacceptimage/*capturecameraonChange{onChangeInput} />1、taro3react 2、taro3vue3