3 Spring和SpringMVC父子容器关系

书接上文,上文提到了一个重要的概念:

Spring 是父容器, Spring MVC是子容器

现在就来仔细研究一下这句话。

Spring和SpringMVC作为Bean管理容器和MVC层的默认框架,已被众多WEB应用采用,而实际使用时,由于有了强大的注解功能,很多基于XML的配置方式已经被替代,但是在实际项目中,同时配置Spring和SpringMVC时会出现一些奇怪的异常,比如Bean被多次加载,多次实例化,或者依赖注入时,Bean不能被自动注入,但是明明你已经将该Bean注册了的。找原因还是要看问题的根源,我们从容器说起。

Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans。

Spring中可以包括多个容器,而且容器有上下层关系,目前最常见的一种场景就是在一个项目中引入Spring和SpringMVC这两个框架,其实就是2个容器,Spring是根容器,SpringMVC是其子容器,并且在Spring根容器中对于SpringMVC容器中的Bean是不可见的,而在SpringMVC容器中对于Spring根容器中的Bean是可见的,也就是子容器可以看见父容器中的注册的Bean,反之就不行。下面是web.xml的配置文件:

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <!-- 配置spring上下文环境 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <!-- spring 配置Listener -->
    <!-- Bootstraps the root web application context before servlet initialization -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 配置springmvc前端控制器    -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation,springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
        </init-param>
        <!-- 设置为负数或不设置时会在servlet第一次用到时调用init()方法 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

在上面的配置中,web启动时ContextLoaderListener被优先初始化时,其会根据<context-param>元素中contextConfigLocation参数指定的配置文件路径,在这里就是"/WEB-INF/applicationContext.xml”,来创建WebApplicationContext实例,并调用ServletContext的setAttribute方法,将其设置到ServletContext中,属性的key为”org.springframework.web.context.WebApplicationContext.ROOT”,最后的”ROOT"字样表明这是一个 Root WebApplicationContext。

控制台日志 

DispatcherServlet在初始化时,会根据<init-param>元素中contextConfigLocation参数指定的配置文件路径,即"/WEB-INF/dispatcher-servlet.xml”,来创建WebApplicationContext。同时,其会判断WebApplicationContext是否存在,如果存在,则将其设置为自己的parent。这就是父子上下文(父子容器)的概念。

FrameworkServlet(DispatcherServlet的父类)中的initServletBean方法:

控制台日志

在此过程中还会将所有的的handler添加到 handlerMap中,AbstractUrlHandlerMapping.class:

    protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
        Assert.notNull(urlPath, "URL path must not be null");
        Assert.notNull(handler, "Handler object must not be null");
        Object resolvedHandler = handler;
        if (!this.lazyInitHandlers && handler instanceof String) {
            String handlerName = (String)handler;
            if (this.getApplicationContext().isSingleton(handlerName)) {
                resolvedHandler = this.getApplicationContext().getBean(handlerName);
            }
        }

        Object mappedHandler = this.handlerMap.get(urlPath);
        if (mappedHandler != null) {
            if (mappedHandler != resolvedHandler) {
                throw new IllegalStateException("Cannot map " + this.getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + this.getHandlerDescription(mappedHandler) + " mapped.");
            }
        } else if (urlPath.equals("/")) {
            if (this.logger.isInfoEnabled()) {
                this.logger.info("Root mapping to " + this.getHandlerDescription(handler));
            }

            this.setRootHandler(resolvedHandler);
        } else if (urlPath.equals("/*")) {
            if (this.logger.isInfoEnabled()) {
                this.logger.info("Default mapping to " + this.getHandlerDescription(handler));
            }

            this.setDefaultHandler(resolvedHandler);
        } else {
            this.handlerMap.put(urlPath, resolvedHandler);
            if (this.logger.isInfoEnabled()) {
                this.logger.info("Mapped URL path [" + urlPath + "] onto " + this.getHandlerDescription(handler));
            }
        }

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值