定西市小企业网站建设建设,做网站怎么收费多少,个人网站赏析,深圳工业设计展20221.背景在使用Spring MVC时候大部分同学都会定义两个配置文件#xff0c;一个是Spring的配置文件spring.xml#xff0c;另一个是Spring MVC的配置文件spring-mvc.xml。在这里给大家抛个问题#xff0c;如果在spring.xml和spring-mvc.xml文件中同时定义一个相同id的单例bean会…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相关配置。servletservlet-namespring-mvc/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-classinit-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:spring-mvc.xml/param-value/init-paramload-on-startup1/load-on-startup
/servlet配置很简单只是配置了一个类型为DispatcherServlet类型的Servlet,并设置了初始化参数。那DispatcherServlet是什么呢2.2 DispatcherServlet类介绍查看API文档 从继承图看出最终继承自HttpServlet其实就是一个普通的Servlet。那为什么这个Servlet就能完成Spring MVC一系列复杂的功能呢继续往下看。2.3 DispatcherServlet工作流程 DispatcherServlet工作流程如下 (1) 所有请求先发到DispacherServlet 。 (2) DispacherServlet根据请求地址去查询相应的Controller然后返回给DispacherServlet。 (3) DispacherServlet得到Controller后让Controler处理相应的业务逻辑。 (4) Controler处理处理完后将结果返回给DispacherServlet。 (5) DispacherServlet把得到的结果用视图解析器解析后获得对应的页面。 (6) DispacherServlet跳转到解析后的页面。在整个过程中DispatcherServlet承当了一个中心控制器的角色来处理各种请求。2.4 DispatcherServlet上下文继承关系上图来自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.结合图和上述文字我们可以得出以下信息 应用中可以包含多个IOC容器。 DispatcherServlet的创建的子容器主要包含Controller、view resolvers等和web相关的一些bean。 父容器root WebApplicationContex主要包含包含一些基础的bean比如一些需要在多个servlet共享的dao、service等bean。 如果在子容器中找不到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会怎样假设idtest。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-packagexx.xx.xx/那如果在spring.xml和spring-mvc.xml配置了重复的包会怎样呢如果本文看明白的同学此时已经知道了答案。答案是会在两个父子IOC容器中生成大量的相同bean这就会造成内存资源的浪费。也许有同学想到那只在spring.xml中设置扫描包不就能避免这种问题发生了吗答案是这样吗大家可以试试这样会有什么问题。如果不行那是为什么呢欲知分晓敬请期待下篇分解如果想获得更多欢迎关注公众号七分熟pizza公众号里我会分享更多技术以及职场方面的经验大家有什么问题也可以直接在公众号向我提问交流。