问题1 如何让web容器加载你的web MVC框架

对于基于servlet的web容器来说,遵循的是servlet规范,入口配置文件是web.xml。这类web容器会在启动的时候会而且仅会加载如下三种类型的对象:

  1. servlet
  2. context listener
  3. filter

而且有一定的加载和销毁顺序!

Loading Servlets, Context Listeners, and Filters

Servlets, Context Listeners, and Filters are loaded and destroyed in the following order:

Order of loading:

  1. Context Listeners
  2. Filters
  3. Servlets

Order of destruction:

  1. Servlets
  2. Filters
  3. Context Listeners

Servlets and filters are loaded in the same order they are defined in the web.xml file and unloaded in reverse order. Context listeners are loaded in the following order:

  1. All context listeners in the web.xml file in the order as specified in the file
  2. Packaged JAR files containing tag library descriptors
  3. Tag library descriptors in the WEB-INF directory

一般来说servlet用于接收用户请求,filter作为servlet的拦截器,context listener则作为事件监听器。所以一般都是使用servlet来加载web MVC框架。

对于Spring MVC来说,官方文档有很详细的描述:15. Web MVC framework

spring MVC是通过DispatcherServlet这个Front Controller启动的。而DispatcherServlet本身就是一个servlet,配置在web.xml中:

<servlet>
<servlet-name>foobar</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>foobar</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

问题2:你的web MVC框架又是怎么加载你的web-aware Spring ApplicationContext?

前面简单配置我们已经让web容器加载我们的web MVC框架启动类DispatcherServlet了,那么DispatcherServlet又是怎么加载我们的应用上下文呢?

在Spring MVC官方文档 15. Web MVC framework 中有这么一句话:

As detailed in Section 3.13, “Additional Capabilities of the ApplicationContext”, ApplicationContext instances in Spring can be scoped. In the Web MVC framework, each DispatcherServlet has its own WebApplicationContext, which inherits all the beans already defined in the root WebApplicationContext. These inherited beans can be overridden in the servlet-specific scope, and you can define new scope-specific beans local to a given servlet instance.

正如web容器的入口配置文件是web.xml一样,每个web MVC框架也都有自己的配置文件,对于SpringMVC来说,其配置文件默认为WEB-INF/${dispatcherServletName}-servlet.xml。对于上面的例子,DispatchServlet载入后,它将从foobar-servlet.xml中加载应用上下文。

分解应用上下文

根据前面的配置,DispatcherServlet已经载入foobar-servlet.xml。你可以将系统中所有的bean都配置在foobar-servlet.xml中,但是最后这个文件会非常臃肿,最佳实践是对每一层(web、biz、dal)进行单独配置,至少要区分web层配置和biz层的配置。

为了保证所有的配置文件都可以被载入,我们需要在web.xml文件中配置一个上下文载入器。

Configuring a context loader

To ensure that all of these configuration files are loaded, you’ll need to configure a context loader in your web.xml file. A context loader loads context configura- tion files in addition to the one that DispatcherServlet loads. The most com- monly used context loader is a servlet listener called ContextLoaderListener that is configured in web.xml as follows:

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

NOTE Some web containers do not initialize servlet listeners before servlets— which is important when loading Spring context definitions. If your application is going to be deployed to an older web container that adheres to Servlet 2.2 or if the web container is a Servlet 2.3 container that does not initialize listeners before servlets, you’ll want to use ContextLoaderServlet instead of ContextLoaderListener.

配置好ContextLoaderListener之后,我们需要告诉它哪些文件需要它加载,否者默认它会加载/WEB-INF/applicationContext.xml,这是通过设置contextConfigLocation parameter in the servlet context:

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/foobar-service.xml
/WEB-INF/foobar-data.xml
/WEB-INF/foobar-security.xml
</param-value>
</context-param>

为什么不使用Spring的import标签引入多个文件 How To Load Multiple Spring Bean Configuration File

File : Spring-All-Module.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <import resource="common/Spring-Common.xml"/>
<import resource="connection/Spring-Connection.xml"/>
<import resource="moduleA/Spring-ModuleA.xml"/>

这是因为使用ContextLoaderListener载入的应用上下文会作为DispatcherServlet载入的应用上下文(the WebApplicationContext for this servlet)的根应用上下文(root application context)被所有的DispatherServlet载入上下文共享。

A web application can define any number of DispatcherServlets. Each servlet will operate in its own namespace, loading its own application context with mappings, handlers, etc. Only the root application context as loaded byorg.springframework.web.context.ContextLoaderListener, if any, will be shared.

public class DispatcherServlet extends FrameworkServlet {
...
} public abstract class FrameworkServlet extends HttpServletBean { @Override
protected final void initServletBean() throws ServletException {
… try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}

} protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
// No fixed context defined for this servlet - create a local one.
WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
wac = createWebApplicationContext(parent);
} if (!this.refreshEventReceived) {
// Apparently not a ConfigurableApplicationContext with refresh support:
// triggering initial onRefresh manually here.
onRefresh(wac);
} if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
} return wac;
}
}

其中WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); ContextLoaderListener加载的根应用上下文:

WebApplicationContext org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)

Find the root WebApplicationContext for this web application, which is typically loaded via org.springframework.web.context.ContextLoaderListener.

其实它仅仅是查找ServletContext中有没有key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值:

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
} public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
Assert.notNull(sc, "ServletContext must not be null");
Object attr = sc.getAttribute(attrName);
if (attr == null) {
return null;
}
if (attr instanceof RuntimeException) {
throw (RuntimeException) attr;
}
if (attr instanceof Error) {
throw (Error) attr;
}
if (attr instanceof Exception) {
throw new IllegalStateException((Exception) attr);
}
if (!(attr instanceof WebApplicationContext)) {
throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
}
return (WebApplicationContext) attr;
}

因为ContextLoaderListener加载完后以这个key将它放在了ServletContext中。

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
} public class ContextLoader { public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
} … try {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext); // Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
} ... return this.context;
}
...
}
}

Spring官方文档给出了这么一个图: 

然而这个图是不正确的!在这个图里,DispatcherServlet加载的WebApplicationContext跟biz层的WebApplicationContext(s)(即root ApplicationContext,这个称谓本身就有点怪,有误导嫌疑!),是一对多的关系!其实应该反过来。从上面的代码看来,通过WebApplicationContextUtils.getWebApplicationContext(servletContext)拿到的rootApplicationContext,是所有DispatcherServlet共享的biz层的应用上下文(这点从传递servletContext参数也可以看出来,因为ServletContext本身就是application级别的),它是作为每个DispatcherServlet加载的web ApplicationContext容器的parent上下文,进行共享。

这点可以很简单的通过如下方式进行验证:在web层用WebApplicationContextUtils.getWebApplicationContext看能不能拿到DispatcherServlet所载入的web ApplicationContext。笔者试验了一下,确实拿不到,因为root ApplicationContext(parent)感知不到也不care,每个DispatcherServlet载入的webApplication(children)。

--EOF--

from: http://blog.arganzheng.me/posts/spring-and-web-mvc-integration-web-application-context.html

Spring与web MVC的整合——Spring的应用上下文管理的更多相关文章

  1. Axis2在Web项目中整合Spring

    一.说明: 上一篇说了Axis2与Web项目的整合(详情 :Axis2与Web项目整合)过程,如果说在Web项目中使用了Spring框架,那么又改如何进行Axis2相关的配置操作呢? 二.Axis2 ...

  2. 8 -- 深入使用Spring -- 7...2 MVC框架与Spring整合的思考

    8.7.2 MVC 框架与Spring整合的思考 对于一个基于B/S架构的JAVA EE 应用而言,用户请求总是向MVC框架的控制器请求,而当控制器拦截到用户请求后,必须调用业务逻辑组件来处理用户请求 ...

  3. Spring的Web MVC框架

    以下内容引用自http://wiki.jikexueyuan.com/project/spring/web-mvc-framework.html: Spring web MVC框架提供了模型-视图-控 ...

  4. Spring与Web框架(例如Spring MVC)漫谈——关于Spring对于多个Web框架的支持

    在看Spring MVC的官方文档时,最后一章是关于Spring对于其它Web框架的支持(如JSF,Apache Struts 2.x,Tapestry 5.x),当然Spring自己的MVC框架Sp ...

  5. Spring由于web配置导致的spring配置文件找不到的问题的解决方案

    在把某项技术整合到Spring中的时候,我们时常会发现报如下错误: org.springframework.beans.factory.BeanCreationException: Error cre ...

  6. [Spring] 学习Spring Boot之二:整合MyBatis并使用@Trasactional管理事务

    一.配置及准备工作 1.在 Maven 的 pom 文件中新增以下依赖: <dependency> <groupId>mysql</groupId> <art ...

  7. spring boot使用freemarker模版整合spring Data JPA

    目录结构 第一步:在pom.xml文件中添加依赖 <!--模板依赖--> <dependency> <groupId>org.springframework.boo ...

  8. Spring 4 官方文档学习 Web MVC 框架

    1.介绍Spring Web MVC 框架 Spring Web MVC 框架是围绕DispatcherServlet设计的,所谓DispatcherServlet就是将请求分发到handler,需要 ...

  9. 从Spring到SpringBoot构建WEB MVC核心配置详解

    目录 理解Spring WEB MVC架构的演变 认识Spring WEB MVC 传统时代的Spring WEB MVC 新时代Spring WEB MVC SpringBoot简化WEB MVC开 ...

随机推荐

  1. 【51nod】1934 受限制的排列

    题解 这题还要判无解真是难受-- 我们发现我们肯定能确定1的位置,1左右的两个区间是同理的可以确定出最小值的位置 我们把区间最小值看成给一个区间+1,构建出笛卡尔树,就求出了每一次取最小值和最小值左右 ...

  2. js获取单个查询串的值

    function getSearchString(key) { // 获取URL中?之后的字符 var searchArr= window.location.href.split('#')[0].sp ...

  3. 【运维理论】RAID级别简介

    独立硬盘冗余阵列(RAID, Redundant Array of Independent Disks),旧称廉价磁盘冗余阵列(RAID, Redundant Array of Inexpensive ...

  4. TPS和QPS是什么,他们的区别是什么

    一.TPS:Transactions Per Second(每秒传输的事物处理个数),即服务器每秒处理的事务数.TPS包括一条消息入和一条消息出,加上一次用户数据库访问.(业务TPS = CAPS × ...

  5. Swift2.0语言教程之闭包

    Swift2.0语言教程之闭包 Swift2.0语言闭包 闭包是自包含的函数代码块,可以在代码中被传递和使用.Swift中的闭包与C和Objective-C中的代码块(blocks)以及其他一些编程语 ...

  6. [ 转载 ] Okhttp的用法

    Android中OkHttp的使用 LuckyXiang 简书作者 02018-01-18 19:04 打开App Android中OkHttp的使用 官方网站 | Javadoc 1 简介 OkHt ...

  7. SQLSERVER2014集群实战——DNS的坑

    近几日生产环境总是偶发的出现数据库连接失败的错误,一开始并未引起重视,因为反馈的人很少,而且应用服务器与数据库服务器都处在同一机房的内网环境,相互之间的访问应该是很稳定的.直到早上有几分钟的时间里出现 ...

  8. 利用meterpreter下的Venom免杀后门

    转载请注明作者:admin-神风 下载地址:https://github.com/r00t-3xp10it/venom .从Github上下载框架: tar.gz OR zip OR git clon ...

  9. 使用 Nokia Imaging SDK 开发有滤镜功能的 Windows Phone 8 应用

    说到滤镜应用,相信很多数开发者都对照片特效的经验都十分有限,通常都是去找一些三方的类库进行学习或移植,今天在这里给大家介绍下 Nokia 的 Imaging SDK, 相信大家对Nokia的自家图像软 ...

  10. An ac a day,keep wa away

    zoj 初学者题: 1001 1037 1048 1049 1051 1067 1115 1151 1201 1205 1216 1240 1241 1242 1251 1292 1331 1334 ...