Spring源码分析3----Web环境中的SpringMVC(Web容器中的上下文的设计)
来源:互联网 发布:php url传递参数 编辑:程序博客网 时间:2024/04/30 02:37
一、Web环境中的Spring MVC
用大体上看,在使用Spring MVC的时候,需要在web.xml中配置DispatcherServlet,这个DispatcherServlet可以看成是一个前端控制器的具体实现,还需要在Bean定义中配置Web请求和COntroller(控制器)的对应关系,以及各种视图的展现方式。Spring IoC是一个独立的模块,它并不是直接在Web容器中发挥作用的,如果要在Web环境中使用IoC容器,需要Spring为IoC设计一个启动过程,把IoC容器导入,并在Web容器中建立起来。具体来说,这个启动过程是和Web容器的启动过程集成在一起的。在这个过程中,一方面处理Web容器的启动,另一方面通过设计特定的Web容器拦截器,将IoC容器载入到Web环境中来,并将其初始化。在这个过程建立完成以后,IoC容器才能正常工作,而Spring MVC是建立是IoC容器的基础上的,这样才能建立起MVC框架的运行机制,从而响应从Web容器传递的HTTP请求。
下面以Tomcat作为Web容器的例子进行分析。
…… <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class><listener>……作为Spring MVC的启动类,ContextLoaderListener被定义为一个监听器,这个监听器是与Web服务器的生命周期相关联的,由ContextLoaderListener监听器负责完成IoC容器在Web环境中的启动工作。
二、上下文在Web容器中的启动
2.1.IoC容器启动的基本过程
IoC容器的启动过程就是建立上下文的过程,该上下文是与ServletContext相伴而生的,同时也是IoC容器在Web应用环境中的具体表现之一。由ContextLoaderListener启动的上下文为根上下文。在根上下文的基础上,还有一个与Web MVC相碰的上下文 用来保持控制器(DispatchServlet)需要的MVC对象,作为根上下文的子上下文,构成一个层次化的上下文体系。在Web容器中启动Spring应用程序时,首先建立根上下文,然后建立这个上下文体系的,这个上下文体系的建立是由COntextLoader来完成的,具体过程由下:public class ContextLoaderListener extends ContextLoader implements ServletContextListener {public ContextLoaderListener() {}public ContextLoaderListener(WebApplicationContext context) {super(context);}@Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext()); // ------->}@Overridepublic void contextDestroyed(ServletContextEvent event) {closeWebApplicationContext(event.getServletContext());ContextCleanupListener.cleanupAttributes(event.getServletContext());}}
//-------------------------------// public class ContextLoader public WebApplicationContext initWebApplicationContext(ServletContext 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!");}Log logger = LogFactory.getLog(ContextLoader.class);servletContext.log("Initializing Spring root WebApplicationContext");if (logger.isInfoEnabled()) {logger.info("Root WebApplicationContext: initialization started");}long startTime = System.currentTimeMillis();try {// Store context in local instance variable, to guarantee that// it is available on ServletContext shutdown.if (this.context == null) {// 1----------------------->this.context = createWebApplicationContext(servletContext); }if (this.context instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;if (!cwac.isActive()) {// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {// 这里载入根上下文的双亲上下文 --------------------ApplicationContext parent = loadParentContext(servletContext);cwac.setParent(parent);}// IoC的refresh在这里面 2------------------->configureAndRefreshWebApplicationContext(cwac, servletContext);}}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);}if (logger.isDebugEnabled()) {logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");}if (logger.isInfoEnabled()) {long elapsedTime = System.currentTimeMillis() - startTime;logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");}return this.context;}catch (RuntimeException ex) {logger.error("Context initialization failed", ex);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);throw ex;}catch (Error err) {logger.error("Context initialization failed", err);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);throw err;}}/* 1创建根上下文*/protected WebApplicationContext createWebApplicationContext(ServletContext sc) {// 判断使用什么样的类在Web容器作为IoC容器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);}/* 确认使用的IoC容器 应用可以在部署描述中指定使用什么样的IoC容器,是能通过CONTEXT_CLASS_PARAM参数的设置来完成的, 如果没有,则使用默认的IoC容器*/protected Class<?> determineContextClass(ServletContext servletContext) {// 设置指定的IoC容器String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);// 如果要ServletContext中配置了需要使用的CONTEXT_CLASS_PARAM,那就使用这个class,// 当然前提是这个class是可用的if (contextClassName != null) {try {return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());}catch (ClassNotFoundException ex) {throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", ex);}}else { // 如果没有额外的配置,那么使用默认的contextClasscontextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());try {return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());}catch (ClassNotFoundException ex) {throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", ex);}}}// 2protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {if (ObjectUtils.identityToString(wac).equals(wac.getId())) {String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);if (idParam != null) {wac.setId(idParam);}else {wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(sc.getContextPath()));}}wac.setServletContext(sc);String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);}ConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(sc, null);}customizeContext(sc, wac);wac.refresh(); // ------------------>}
这就是IoC容器在Web容器中的启动过程,与在应用中启动IoC容器的方式相类似,所不同的是这里需要考虑Web容器的环境特点,比如各种参数的设置、IoC容器与Web容器ServletContext的结合等。在初始化这个上下文以后,该上下文会被存储到ServletContext中,这样就建立了一个全局的关于整个应用的上下文。同时在启动Spring MVC时,我们还会看到这个上下文被以后的DispatcherServlet在进行自己持有的上下文的初始化时,设置为DispatcherServlet自带的上下文的双亲上下文。这个过程在下面分析Spring MVC实现的地文清楚地看到
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {String CONFIG_LOCATION_DELIMITERS = ",; \t\n";String CONVERSION_SERVICE_BEAN_NAME = "conversionService";String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";String ENVIRONMENT_BEAN_NAME = "environment";String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";void setId(String id);void setParent(ApplicationContext parent);@OverrideConfigurableEnvironment getEnvironment();void setEnvironment(ConfigurableEnvironment environment);void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);void addApplicationListener(ApplicationListener<?> listener);void addProtocolResolver(ProtocolResolver resolver);// --------------------->void refresh() throws BeansException, IllegalStateException;void registerShutdownHook();@Overridevoid close();boolean isActive();ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;}
//------------------------------------------------------------// public abstract class AbstractApplicationContext extends DefaultResourceLoader//implements ConfigurableApplicationContext, DisposableBean@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {prepareRefresh();ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();prepareBeanFactory(beanFactory);try {postProcessBeanFactory(beanFactory);invokeBeanFactoryPostProcessors(beanFactory);registerBeanPostProcessors(beanFactory);initMessageSource();initApplicationEventMulticaster();onRefresh();registerListeners();finishBeanFactoryInitialization(beanFactory);finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}destroyBeans();cancelRefresh(ex);throw ex;}finally {resetCommonCaches();}}}在Web.xml,已经配置了ContextLoaderListener,这个ContextLoaderListener是Spring提供的类,是为在Web容器中建立IoC容器服务的,它实现了ServletContextListener接口,这个接口提供了与Servlet生命周期的回调。而在Web容器中,建立WebApplicationContext的过程,是在contextInitialized的接口实现中完成的。具体的载入IoC容器的过程是由ContextLoaderListener交由ContextLoaderg来完成的,而ContextLoader本身就是ContextLoaderListener的基类。
在ContextLoader中,完成了两个IoC容器建立的基本过程,一个是在Web容器中建立起双亲IoC容器,另一个是生成相应的WebApplicationContext并将其初始化。
2.2.web容器中的上下文设计
先从Web容器的上下文入手,看看Web环境中的上下文设置有哪些特别之处,然后再到ContextLoaderListener中去了解整个容器启动的过程。public interface WebApplicationContext extends ApplicationContext {// 这里定义的常量用于在ServletContext中存取根上下文String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";String SCOPE_REQUEST = "request";String SCOPE_SESSION = "session";String SCOPE_GLOBAL_SESSION = "globalSession";String SCOPE_APPLICATION = "application";String SERVLET_CONTEXT_BEAN_NAME = "servletContext";String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";// 对WebApplicationContext来说,需要得到Web容器的ServletContext,// 通过这个方法可以取得Web容器的ServletContextServletContext getServletContext();}在启动过程中,Spring会使用一个默认的WebApplicationContext实现作为IoC容器。这个默认使用的IoC容器就是XmlWebApplicationContext,
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {/** 这里是设置默认BeanDefinition的地方,在/WEB-INF/applicationContext.xml文件中, 如果不特别指定其他文件,IoC容器会从这里读取BeanDefinition来初始化IoC容器 */public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";/** 默认的配置文件位置在/WEB-INF/目录下 */public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";/** 默认的配置文件后缀名.xml文件 */public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";// 像前面对IoC容器的分析一样,这个加载过程在容器refresh()时启动@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 对于XmlWebApplicationCOntext,当然是使用XmlBeanDefinitionReader来对BeanDefinition信息进行解析XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// Configure the bean definition reader with this context's// resource loading environment.beanDefinitionReader.setEnvironment(getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// Allow a subclass to provide custom initialization of the reader,// then proceed with actually loading the bean definitions.initBeanDefinitionReader(beanDefinitionReader);loadBeanDefinitions(beanDefinitionReader);}protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {}/* 如果有多个BeanDefinition的文件定义,需要逐个载入,都是通过reader来完成的, 这个初始化过程是由refreshBeanFactory方法来完成的,这里只负责载入BeanDefinition*/protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {String[] configLocations = getConfigLocations();if (configLocations != null) {for (String configLocation : configLocations) {reader.loadBeanDefinitions(configLocation);}}}/* 这里取得Resource位置的地方,使用了设定的默认配置位置, 默认的配置是/WEB_INF/applicationContext.xml */@Overrideprotected String[] getDefaultConfigLocations() {if (getNamespace() != null) {return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};}else {return new String[] {DEFAULT_CONFIG_LOCATION};}}}从代码中可以看到,在XmlWebApplication中,基本的上下文功能都已经通过类的继承获得,这里需要处理的是,如何获取Bean定义信息,在这里,就转化为如何在Web容器环境如这里指定的/WEB_INF/applicationContext.xml中获得Bean定义信息。在获得Bean定义信息之后,后面的过程基本上就和前面分析的XmlFileSystemBeanFactory一样,是通过XmlBeanDefinitionReader来载入Bean定义信息的,最终完成整个上下文的初始化过程.
3.ContextLoader的设计与实现
对于Spring承载的Web应用而言,可以指定在Web应用程序启动时载入IoC容器(或者称为WebApplicationContext).这个功能是由ContextLoaderListener这样的类来完成的,这旨在Web容器中配置的监听器。这个ContextLoaderListener通过使用ContextLoader来完成实际的WebApplicationContext, 也就是IoC容器的初始化工作。这个ContextLoader就像Spring应用程序在Web容器中的启动器。这个启动过程是在Web容器中发生的,所有需要根据Web容器部署的要求来定义ContextLoader,相关的配置在概述中已经看到了,这里就不重复了。为了了解IoC容器在Web容器中的启动原理,这里对启动器ContextLoaderListener的实现进行分析。这个监听器是启动根IoC容器并把它载入到Web容器的主要功模块,也就是整个Spring Web应用加载IoC的第一个地方。从加载过程可以看到,首先从Servlet事件中得到ServletContext,然后可以读取配置在web.xml中的各个相关属性值,接着ContextLoader会实例化WebApplicationContext,并完成其载入和初始化过程。这个被初始化的第一个上下文作为根上下文而存在,这个根上下文载入后,被绑定到Web应用程序的ServletContext上。任何需要访问根上下文的应用程序代码都可以从WebApplicationContextUtils类的静态方法中得到,具体取得根上下文的方法如下:
WebApplicationContext getWebApplicationContext(ServletContext sc);
下面分析具体的根上下文的载入过程。在ContextLoaderListener中,实现的是ServletContextListener接口,由于这个接口是ServletContext的监听者,如果ServletContext发生变化,会触发相应的事件,而监听器一直对这些事件进行监听,如果接收到了监听的事件,就会做出预先设计好的响应动作。下面看看服务器启动时ContextLoaderListener的调用完成了什么,如下代码。在这个初始化回调中,创建了ContextLoader,同时会利用创建出来的ContextLoader来完成IoC容器的初始化
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {public ContextLoaderListener() {}public ContextLoaderListener(WebApplicationContext context) {super(context);}@Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext()); // ---->}@Overridepublic void contextDestroyed(ServletContextEvent event) {closeWebApplicationContext(event.getServletContext());ContextCleanupListener.cleanupAttributes(event.getServletContext());}}下面的分析在上面的“IoC在web容器中的启动"已经分析了,这里就不分析了
0 0
- Spring源码分析3----Web环境中的SpringMVC(Web容器中的上下文的设计)
- Spring IoC容器在Web环境中的启动源码阅读
- Spring、SpringMVC及web容器的Context上下文
- spring中的web上下文,spring上下文,springmvc上下文区别(超详细)
- spring中的web上下文,spring上下文,springmvc上下文区别(超详细)
- spring中的web上下文,spring上下文,springmvc上下文区别(超详细)
- Spring源码分析之ioc容器第二天之oC容器在Web容器中的启动
- Spring boot源码分析-AnnotationConfigEmbeddedWebApplicationContext默认web环境下的启动容器(3)
- web上下文,spring上下文,springmvc上下文
- spring上下文在web.xml中的配置
- spring上下文在web.xml中的配置
- web spring springmvc上下文关系
- Spring boot源码分析-AnnotationConfigApplicationContext非web环境下的启动容器(2)
- SPRING技术内幕-笔记(七)WEB环境中的springMVC
- Spring MVC 根应用上下文在Web容器中的启动及其销毁
- 【Spring源码解析】之IOC容器在Web容器中的启动
- Spring源码学习(二):IOC容器在WEB容器中的初始化
- Spring在Web容器中的启动过程
- Mac用户福利,学会这些快捷键,瞬间提高逼格
- 计算机视觉、模式识别、机器学习常用牛人主页链接
- Android几种进程
- VSS自动登录设置
- PAT 1087【二级最短路】
- Spring源码分析3----Web环境中的SpringMVC(Web容器中的上下文的设计)
- C/C++中cin,cin.getline,getline的用法
- 用struts2 s2-045漏洞拿站记录
- 关于写入MySQL数据库乱码问题
- LoRaWAN 定位
- mac下配置mysql
- Cocos2dx项目移植Android平台
- python自然语言处理学习资源汇总
- 展会介绍