boot spring 没有父子容器_Spring 系列(二):Spring MVC的父子容器

e79797c7639a682c8454b49b3eaa35d1.png

1.背景

在使用Spring MVC时候大部分同学都会定义两个配置文件,一个是Spring的配置文件spring.xml,另一个是Spring MVC的配置文件spring-mvc.xml。

在这里给大家抛个问题,如果在spring.xml和spring-mvc.xml文件中同时定义一个相同id的单例bean会怎样呢?大家可以先思考一下再继续往下看。

我做了个实验,结论是:容器中会同时存在两个相同id 的bean,而且使用起来互不干扰。

这是为什么呢?学过Spring的同学肯定会质疑,众所周知id是bean的唯一标示,怎么可能同时存在两个相同id的bean呢?是不是我在胡扯呢?

原谅我在这和大家卖了个关子,其实大家说的都没错,因为这里涉及到Spring MVC父子容器的知识点。

这个知识点是:在使用Spring MVC过程中会存在Spring MVC 、Spring两个IOC容器,且Spring MVC是Spring的子容器。

那这个父子容器到底是什么呢?

为了保证我所说的权威性,而不是知识的二道贩子,我将从Spring 官方文档和源码两方面展开介绍。

2.Spring MVC父子容器

2.1 web.xml配置

还是先找程序入口,查看web.xml配置文件,找到Spring MVC相关配置。

<servlet><servlet-name>spring-mvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>

配置很简单,只是配置了一个类型为DispatcherServlet类型的Servlet,并设置了初始化参数。那DispatcherServlet是什么呢?

2.2 DispatcherServlet类介绍

查看API文档

74e3f3ff36d48fdc40e2f9b2e29775e3.png

从继承图看出最终继承自HttpServlet,其实就是一个普通的Servlet。那为什么这个Servlet就能完成Spring MVC一系列复杂的功能呢?继续往下看。

2.3 DispatcherServlet工作流程

769ff1f7df0de658847cabb78e304013.png

DispatcherServlet工作流程如下:

(1) 所有请求先发到DispacherServlet 。

(2) DispacherServlet根据请求地址去查询相应的Controller,然后返回给DispacherServlet。

(3) DispacherServlet得到Controller后,让Controler处理相应的业务逻辑。

(4) Controler处理处理完后将结果返回给DispacherServlet。

(5) DispacherServlet把得到的结果用视图解析器解析后获得对应的页面。

(6) DispacherServlet跳转到解析后的页面。

在整个过程中DispatcherServlet承当了一个中心控制器的角色来处理各种请求。

2.4 DispatcherServlet上下文继承关系

2384a12957435ba27d4af611e1b5d008.png

上图来自Spring官网:

https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html

从图中可以看到DispatcherServlet里面有一个 Servlet WebApplicationContext,继承自 Root WebApplicationContext。

从上篇文章中我们知道WebApplicationContext其实就是一个IOC容器,root WebApplicationContext是Spring容器。

这说明DispatcherServlet中里创建了一个IOC容器并且这个容器继承了Spring 容器,也就是Spring的子容器。

而且官方文档中还有如下一段文字描述:

For many applications, having a single WebApplicationContext is simple and suffices. It is also possible to have a context hierarchy where one root WebApplicationContext is shared across multiple DispatcherServlet (or other Servlet) instances, each with its own child WebApplicationContext configuration. See Additional Capabilities of the ApplicationContext for more on the context hierarchy feature.The root WebApplicationContext typically contains infrastructure beans, such as data repositories and business services that need to be shared across multiple Servlet instances.
Those beans are effectively inherited and can be overridden (that is, re-declared) in the Servlet-specific child WebApplicationContext, which typically contains beans local to the given Servlet.

结合图和上述文字我们可以得出以下信息:

  1. 应用中可以包含多个IOC容器。
  2. DispatcherServlet的创建的子容器主要包含Controller、view resolvers等和web相关的一些bean。
  3. 父容器root WebApplicationContex主要包含包含一些基础的bean,比如一些需要在多个servlet共享的dao、service等bean。
  4. 如果在子容器中找不到bean的时候可以去父容器查找bean。

看到这里也许大家心中也许就明白文章开头中我说的Spring MVC中的父子容器了,对那个问题也有了自己的判断和答案。

当然文章还没有结束,毕竟这还仅限于对官方文档的理解,为了进一步验证,我们拿出终极武器:

阅读源码!

2.5 DispatcherServlet源码分析

本小节我们分为Spring MVC容器的创建和bean的获取两部分进行分析。

2.5.1 Spring MVC容器的创建

前面分析到DispatcherServlet本质上还是一个Servlet ,既然是Servlet ,了解Servlet生命周期的同学都知道Web 容器装载Servlet第一步是执行init()函数,因此以DispatcherServlet 的init函数为突破口进行分析。

@Override
public final void init() throws ServletException {// 1.读取init parameters 等参数,其中就包括设置contextConfigLocation PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);//2.初始化servlet中使用的beaninitServletBean();
}

在第1步读取init parameter的函数最终会调用setContextConfigLocation()设置配置文件路径。此处重点介绍initServletBean(),继续跟踪。

Override
protected final void initServletBean() throws ServletException {//初始化webApplicationContextthis.webApplicationContext = initWebApplicationContext();
}
protected WebApplicationContext initWebApplicationContext() {//1.获得rootWebApplicationContextWebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;//2.如果还没有webApplicatioinContext,创建webApplicationContextif (wac == null) {//创建webApplicationContextwac = createWebApplicationContext(rootContext);}return wac;
}

可以看到上面初始化webApplicationContext分为2步。

(1)获取父容器rootWebApplicationContext。

(2)创建子容器。

我们先看看rootWebApplicationContext是如何获取的。

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {Object attr = sc.getAttribute(attrName);return (WebApplicationContext) attr;
}

从上面代码中我没看到是从ServletContext获取了名为“WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE”的webApplicationContext。

认真看过上篇文章的同学应该记得这个属性是在Spring初始化 容器initWebApplicationContext()函数中的第3步设置进去的,取得的值即Spring IOC容器。

继续看如何创建webApplicationContext。

protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {return createWebApplicationContext((ApplicationContext) parent);
}
createWebApplicationContext(ApplicationContext parent) {//1.获取WebApplicationContext实现类,此处其实就是XmlWebApplicationContextClass<?> contextClass = getContextClass();//生成XmlWebApplicationContext实例ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);//2.设置rootWebApplicationContext为父容器 wac.setParent(parent);//3.设置配置文件wac.setConfigLocation(getContextConfigLocation());//4.配置webApplicationContext.configureAndRefreshWebApplicationContext(wac);return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {//开始处理beanwac.refresh();
}

看到这里同学们有没有是曾相识的感觉。是的,这段逻辑和上篇文章创建Spring IOC的逻辑类似。

唯一不同的是在第2步会把Spring容器设置为自己的父容器。至于新建容器中bean的注册、解析、实例化等流程和Spring IOC容器一样都是交给XmlWebApplicationContext类处理,还没有掌握的同学可以看上篇文章。

2.5.2 Spring MVC Bean的获取

Spring MVC bean的获取其实我们在上篇文章已经介绍过,这次再单拎出来介绍一下,加深记忆。

protected <T> T doGetBean(// 获取父BeanFactoryBeanFactory parentBeanFactory = getParentBeanFactory();//如果父容器不为空,且本容器没有注册此bean就去父容器中获取beanif (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// 如果父容器有该bean,则调用父beanFactory的方法获得该beanreturn (T) parentBeanFactory.getBean(nameToLookup,args);}//如果子容器注册了bean,执行一系列实例化bean操作后返回bean.//此处省略实例化过程.....return (T) bean;
}

上面代码就可以对应官方文档中“如果子容器中找不到bean,就去父容器找”的解释了。

3.小结

看完上面的介绍,相信大家对Spring MVC父子容器的概念都有所了解,现在我们分析文章开头的问题。

如果spring.xml和spring-mvc.xml定义了相同id的bean会怎样?假设id=test。

1.首先Spring 初始化,Spring IOC 容器中生成一个id为test bean实例。

2.Spring MVC开始初始化,生成一个id为test bean实例。

此时,两个容器分别有一个相同id的bean。那用起来会不会混淆?

答案是不会。

当你在Spring MVC业务逻辑中使用该bean时,Spring MVC会直接返回自己容器的bean。

当你在Spring业务逻辑中使用该bean时,因为子容器的bean对父亲是不可见的,因此会直接返回Spring容器中的bean。

虽然上面的写法不会造成问题。但是在实际使用过程中,建议大家都把bean定义都写在spring.xml文件中。

因为使用单例bean的初衷是在IOC容器中只存在一个实例,如果两个配置文件都定义,会产生两个相同的实例,造成资源的浪费,也容易在某些场景下引发缺陷。

4.尾声

现在大家基本都不使用在xml文件中定义bean的形式,而是用注解来定义bean,然后在xml文件中定义扫描包。如下:

<context:component-scan base-package="xx.xx.xx"/>

那如果在spring.xml和spring-mvc.xml配置了重复的包会怎样呢?

如果本文看明白的同学此时已经知道了答案。

答案是会在两个父子IOC容器中生成大量的相同bean,这就会造成内存资源的浪费。

也许有同学想到,那只在spring.xml中设置扫描包不就能避免这种问题发生了吗,答案是这样吗?

大家可以试试,这样会有什么问题。如果不行,那是为什么呢?

欲知分晓,敬请期待下篇分解!

如果想获得更多,欢迎关注公众号:七分熟pizza

公众号里我会分享更多技术以及职场方面的经验,大家有什么问题也可以直接在公众号向我提问交流。

a39d55d111fed42ff4bca168e6e39559.png

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

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

相关文章

Linux创建目录【命令】

创建一个hello目录 mkdir /hello -------------------------------------- mkdir 相当于 make directory 相对路径&#xff1a;不从/开始&#xff0c;而是从当前目录开始&#xff0c;例如&#xff1a;data/ ,mnt/zmg绝对路径&#xff1a;从/开始的目录&#xff0c;就叫绝对…

php string常用函数

<?php$a[]a;$a[]b;$a[]C;echo "</br>";/* implode — 将一个一维数组的值转化为字符串说明string implode ( string $glue , array $pieces )string implode ( array $pieces )用 glue 将一维数组的值连接为一个字符串。 参数glue 默认为空的字符串。 pie…

计算机配置的内存的容量为1GB,如果某计算机的内存寻址空间是1GB,那么这台计算机地址总线的线数为()根。A.32B.20C.16D.30 - 试题答案网问答...

相关题目与解析计算机内存的容量大小受到(11)位数的限制。若该总线为20位&#xff0c;可以寻址的内存空间为(12)字节。A&#xff0e;地址某计算机数据总线为8位&#xff0c;地址总线为10位&#xff0c;则CPU可以直接寻址的内存空间范围为______字节。A&#xff0e;28B&#xff…

手术后多久可以做胆摘除_近视手术后多久可以化眼妆?

今天小编就和大家聊聊&#xff0c;做完近视手术后&#xff0c;多久可以画眼妆&#xff1f;很多女生做完手术后&#xff0c;非常关心的一件事情就是多久可以化妆&#xff0c;化妆对手术效果有没有影响&#xff1f;今天&#xff0c;小编就此问题特别咨询了屈光手术专家。专家建议…

Linux创建文件【命令】

在/opt/hello 目录下创建 world.txt 使用命令&#xff1a;touch 文件名 touch world.txt ---------------------------------------- touch “摸”。touch[文件名]&#xff0c;就是摸一下文件&#xff0c;如果文件不存在&#xff0c;就建立新文件。如果存在&#xff0c;就改…

【CentOS 7LAMP架构4】,PHP5和PHP7的安装和配置#171219

2019独角兽企业重金招聘Python工程师标准>>> hellopasswd 安装PHP5 PHP官网www.php.net当前主流版本为5.6/7.1cd /usr/local/srcwget http://cn2.php.net/distributions/php-5.6.30.tar.bz2bzip2 -d php-5.6.30.tar.bz2tar xvf php-5.6.30.tarcd php-5.6.30./config…

Linux删除文件和文件夹【命令】

1、删除文件夹&#xff1a;rm -r /opt/hello 将会删除/opt/hello目录以及其下所有文件、文件夹 2、删除文件&#xff1a;rm -f /opt/hello/world.txt 将会强制删除/opt/hello/world.txt这个文件 使用这个rm 的时候一定要格外小心&#xff0c;linux没有回收站,以至于删除之后…

Linux修改文件内容【命令】

在/opt/hello/world.txt文件中增加一行 hello linux world ! 方法一&#xff1a; 命令是&#xff1a;vi&#xff0c;vim vi 编辑器&#xff0c;相当于记事本&#xff0c;有编辑功能&#xff0c;但较弱 vim 复杂的编辑器&#xff0c;相当于windows的 editplus, notepad 等 …

计算机机房管理具体工作和职责,机房管理

心得体会是指一种读书、实践后所写的感受性文字。体会是指将学习的东西运用到实践中去&#xff0c;通过实践反思学习内容并记录下来的文字。下面是出国留学网小编带来的机房管理工作心得体会范文&#xff0c;欢迎阅读参考。机房管理工作心得体会(一)学校计算机房是信息技术教学…

hadoop中的9000端口代表什么_hadoop服务快速部署

这篇文章记录下针对不同的hadoop版本进行服务部署的过程&#xff0c;希望可以帮到你们安装docker hadoop2.7.0一键部署docker hadoop3.0.0集群(一个master 三个slave)安装docker hadoop 3.2.0 a、docker启动 b、docker compose方式启动安装hadoop 2.7.0版本安装命令docker run …

来自我的破船大大的博客,记录他的iOS成长之路,与君同勉!

注1&#xff1a;这篇文章是我的iOS成长之路系列文章中的第三篇文章&#xff0c;第一篇文章&#xff1a;iOS成长之路-1-入门&#xff0c;第二篇文章&#xff1a;iOS成长之路-2-我的第一个iOS Demo。通过两周时间来学习iOS&#xff0c;我慢慢的叩开了iOS开发的大门&#xff0c;接…

Linux下Oracle移植数据

案例&#xff1a; 老LTESQM数据库下的toolbox用户数据移植到 新LTESQM数据库下的 toolbox 用户。 新LTESQM数据库下还没有toolbox这个用户 开始&#xff1a; 1、查看老LTESQM下toolbox的权限 用oracle数据库dba角色登录&#xff0c;查看用户toolbox的sql。 使用sqldevelo…

迷你世界电锯机器人_迷你世界:生产果冻的机器人,1分钟产出500个,10种口味随意挑选...

迷你世界是一款具有创意性和想象力的3D沙盒游戏&#xff0c;在游戏中玩家们总是能造出各种好玩的建筑物&#xff0c;毕竟背包里拥有成百上千的道具可供玩家们随意使用&#xff0c;而且一些道具相互叠加使用&#xff0c;还能产生更有趣的效果。近日就有一位大神使用多种道具&…

电脑计算机显示向程序发送命令时出现问题,word提示向程序发送命令时出现问题怎么解决 wo...

word提示向程序发送命令时出现问题怎么解决 word提示向程序发送命令时出现问题解决方法 我们的生活中越来越离不开电脑&#xff0c;但在使用电脑的过程中总会遇到各种各样的问题。知识屋致力于给电脑小白朋友们介绍一些简单常用问题的解决方法&#xff0c;这次给大家介绍的是wo…

底层实现红黑树_图解:红黑树

注&#xff1a;本文比较硬核但是很值得大家花心思看完&#xff0c;看完你一定会有所收获的红黑树是面试中一个很经典也很有难度的知识点&#xff0c;网传字节跳动面试官最喜欢问这个问题。很多人会觉得这个知识点太难&#xff0c;不想花太多功夫去了解&#xff0c;也有人会认为…

如何请教问题且以后都会回答你的方法

一、如何请教问题 在网上与人沟通时&#xff0c;学会客气&#xff0c;并且开门见山的把问题说清楚非常必要&#xff0c;还要带上你努力的过程&#xff0c; 并且尽量不要问开放式的问题&#xff0c;例如&#xff1a;你问一句&#xff0c;回答者可能要10句才能解答的问题不要问…

计算机编程方程求解的步骤,计算机解决问题的过程PPT学习课件

《计算机解决问题的过程PPT学习课件》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《计算机解决问题的过程PPT学习课件(17页珍藏版)》请在人人文库网上搜索。1、计算机解决问题的过程包括&#xff1a;编程&#xff0c;1&#xff0c;同&#xff0c;2&#xff0c;1&…

处于停机等非正常状态_一文聊透 Dubbo 优雅停机

1 前言一年之前&#xff0c;我曾经写过一篇《研究优雅停机时的一点思考》&#xff0c;主要介绍了 kill -9&#xff0c;kill -15 两个 Linux 指令的含义&#xff0c;并且针对性的聊到了 Spring Boot 应用如何正确的优雅停机&#xff0c;算是本文的前置文章&#xff0c;如果你对上…

Centos 配置eth0 提示Device does not seem to be present

一.故障现象&#xff1a;[rootc1node01 ~]# service network restartShutting down loopback insterface: [ OK ]Bringing up loopback insterface: [ …

计算机boot进入u盘启动,暗影精灵5怎么设置u盘启动 暗影精灵5设置u盘启动方法...

最近有位电脑用户想要使用u盘启动盘重装系统&#xff0c;但是却不知道应该怎么使用bios设置u盘启动&#xff0c;为此非常苦恼&#xff0c;那么惠普暗影精灵5 omen 15-dc1068tx笔记本怎么使用bios设置u盘启动呢?下面为大家介绍惠普暗影精灵5 omen 15-dc1068tx笔记本使用bios设置…