spring mvc 上下文环境的创建过程
来源:互联网 发布:三菱系统攻丝编程实例 编辑:程序博客网 时间:2024/05/22 10:49
我们都知道web应用的本质上就是一个context容器,一般是一个web应用就有一个context容器,我们想要应用在服务器上运行就需要一个web应用管理容器,这就是tomcat了(当然也有其他的容器,如jetty)。
可能有人会问为什么会需要tomcat容器呢?web应用不能直接部署到服务器吗?web应用直接部署这个当然也是可以的,但是这样我们就需要直接和服务器的操作系统进行交互,每开发一个web应用就要求我们重新写一套和服务器系统交互的程序,这样不符合我们的初衷。而tomcat就能够替我们做这件简单而又繁琐的事,把和服务器交互的程序交给tomcat,而我们只要实现自己的web应用,然后将web应用交给tomcat容器管理即可。
tomcat容器能够统一对web应用进行管理,即能够初始化,维护,销毁web应用。关于tomcat容器如何对web应用进行管理的,以后再做讨论。有兴趣的可以自行研究下。
servlet API中有一个ServletContextListener接口,由它来监听web应用的声明周期。tomcat容器会持有一个ServletContextListener接口实现对象,当web应用启动或者终止时(这两个操作也是由tomcat进行的)会出发一个ServletContextEvent 事件,该事件由ServletContextListener接口实现对象处理。这是典型的观察者模式的应用。
ServletContextListener接口定义了处理ServletContextEvent 事件的两个方法。由实现类根据具体功能来实现。
public interface ServletContextListener extends EventListener { /** * 容器初始化方法--容器启动时初始化根上下文(加载bean) */ public void contextInitialized(ServletContextEvent sce); /** *容器销毁方法() */ public void contextDestroyed(ServletContextEvent sce);}
Root WebApplicationContext初始化
接下来进入正题,学习spring mvc项目容器的启动过程.先看配置文件web.xml.
<!-- 环境参数 加载文件时首先解析 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <!-- spring的监听器 初始化根上下文--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>spring-servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring-servlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
web.xml配置文件由tomcat容器读取,读取顺序context-param –> listener –> filter –> servlet;
tomcat容器解析context-param标签将标签内容以键值对的形式存储在tomcat容器中(即ServletContext对象),在根上下文创建完毕后加载bean时使用.
接下来是加载监听器ContextLoaderListener,它实现了ServletContextListener接口,在容器启动时默认调用其
contextInitialized()方法对根上下文进行初始化。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { /** * * */ @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } /** * Close the root web application context. */ @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); }}
我们发现最终调用了initWebApplicationContext()方法,这个方法是从父类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 { if (this.context == null) { //创建根上下文 this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { if (cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext); } }//将创建好的根上下文注册到servletContext servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); //... return this.context; }
首先保证整个web应用只能有一个跟上下文,如果已经有根上下文存在则会直接抛出异常。
接下来根据servletContext创建一个根web应用上下文,并将其注册到servletContext中.追踪createWebApplicationContext(servletContext)方法:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }
创建根上下文时会根据ServletContext决定其类型,并在创建完成后强制转型为ConfigurableWebApplicationContext对象。
回到ContextLoader.initWebApplicationContext()方法,接下来执行configureAndRefreshWebApplicationContext()方法,读取前面设置的servletContext属性加载spring配置文件(context-param标签配置的),完成对spring相关bean的解析、加载、初始化。
DispatchServlet初始化
在spring mvc框架中,DispatchServlet是核心的servlet,负责请求的分发。在
DispatchServlet的设计中大量使用模板方法模式,所以看源码时我们要小心翼翼的结合DispatchServlet的继承结构来阅读。
tomcat容器在创建一个servlet时会首先调用init()方法来初始化servlet,因此应当首先查找该方法。入口init()方法在父类HttpServletBean中:
public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like. initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
忽略其他的一些初始化设置代码,直接看initServletBean()方法,在FrameworkServlet类中实现:
protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
可以看到核心代码是this.webApplicationContext = initWebApplicationContext(); 作用就是初始化上下文,具体实现:
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger 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; }
最终执行wac = createWebApplicationContext(rootContext);创建一个mvc上下文,创建过程和根上下文创建过程基本一样,最后也会进行spring配置文件bean的解析、加载、初始化。
在完成后会发布一个上下文刷新事件,通知所有的监听器进行处理,最终会调用DispatcherServlet的onfresh()方法初始化mvc相关组件。
- spring mvc 上下文环境的创建过程
- Spring mvc 上下文初始化过程
- Spring MVC 双亲上下文的说明
- Spring MVC 双亲上下文的说明
- spring上下文,spring mvc上下文,以及servlet上下文的关联以及理解
- 转:spring上下文,spring mvc上下文,以及servlet上下文的关联以及理解
- Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析
- Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析
- Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析
- Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析 .
- Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析
- ContextImplAndroid应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析
- Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析
- Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析
- Activity运行上下文的创建过程
- 获取spring环境上下文
- spring mvc 双亲上下文问题
- spring mvc 获取上下文路径
- Data Center Software 协议分析工具的使用
- Truck History POJ
- django虚拟环境搭建
- sum of all integer numbers
- aptche 安装配置教程
- spring mvc 上下文环境的创建过程
- 电话网络 CODEVS
- Flask-Moment
- 利用任务计划实现计算机定时 (休眠唤醒)
- 安全的安装PyCharm 2017.2.3 社区版
- Spark2.x学习笔记:11、RDD依赖关系与stage划分
- Ajax
- bzoj1289: [CTSC2009]移盘子
- 畅通工程再续 HDU