spring之:XmlWebApplicationContext作为Spring Web应用的IoC容器,实例化和加载Bean的过程...

它既是 DispatcherServlet 的 (WebApplicationContext)默认策略,又是 ContextLoaderListener 创建 root WebApplicationContext(根容器,同时也是 DispatcherServlet 的 WebApplicationContext 的父容器)的默认策略。

继承体系

一、XmlWebApplicationContext实例化过程

spring的配置文件加载是以监听的方式加载的xml配置文件

spring-web-4.3.14.RELEASE.jar中的org.springframework.web.context.ContextLoader.java类,通过ContextLoader初始化和销毁Spring Web上下文的过程。
1、ContextLoader类中有一个静态代码块,这个静态代码块就是从配置中读取到“XmlWebApplicationContext”类

    static {// Load default strategy implementations from properties file.// This is currently strictly internal and not meant to be customized// by application developers.try {//DEFAULT_STRATEGIES_PATH = "ContextLoader.properties",即加载的是contextLoader.properties的配置文件了ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);}catch (IOException ex) {throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());}}

"contextLoader.properties"文件就在ContextLoader.class的相同目录中,

contextLoader.properties:(配置中配置的就是XmlWebApplicationContext)

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

2、上面只是将配置读取到ContextLoader中,下面看看XmlWebApplicationContext怎么初始化的,ContextLoader.initWebApplicationContext方法:

org.springframework.web.context.ContextLoader.java

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {//检查是否已经创建了Application context,如果已经存在,抛异常退出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) { //调用createWebApplicationContext,创建XmlWebApplicationContext, this.context = createWebApplicationContext(servletContext); } // 如果当前的应用上下文对象是 ConfigurableWebApplicationContext 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, etc  如果该上下文对象为nul if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> // determine parent for root web application context, if any. //加载父上下文 ApplicationContext parent = loadParentContext(servletContext); // 设置父上下文  cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext); } } //将该上下文对象放入servlet上下文参数中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); //获取当前线程的类加载器 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); // 如果ContextLoader的类加载器和当前线程的类加载器一样,则应用上下文对象赋值给currentContext if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } //否则,就将ContextLoader的类加载器放入到Map中,Map的value是应用上下文对象 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; } }

在ContextLoader.createWebApplicationContext方法中

    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {//获取上下文类Class<?> contextClass = determineContextClass(sc);//如果该上下文类没有实现ConfigurableWebApplicationContext接口则抛出异常        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Custom context class [" + contextClass.getName() +"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");}// 返回该上下文类的实例,调用BeanUtils.instantiateClass(contextClass),通过反射,调用XmlWebApplicationContext的无参构造函数实例化XmlWebApplicationContext对象return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);}

----------------------------BeanUtils.instantiateClass()-----------------------------------------------------------------------------------------------------------------------

这里插入BeanUtils.instantiateClass(),BeanUtils使用instantiateClass初始化对象注意:必须保证初始化类必须有public默认无参数构造器,注意初始化内部类时,内部类必须是静态的,否则报错!

    public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {Assert.notNull(clazz, "Class must not be null");if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {return instantiateClass(clazz.getDeclaredConstructor());}catch (NoSuchMethodException ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {Assert.notNull(ctor, "Constructor must not be null");try {ReflectionUtils.makeAccessible(ctor);return ctor.newInstance(args);}//...
        }@CallerSensitivepublic T newInstance(Object ... initargs)throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException{if (!override) {if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {Class<?> caller = Reflection.getCallerClass();checkAccess(caller, clazz, null, modifiers);}}if ((clazz.getModifiers() & Modifier.ENUM) != 0)throw new IllegalArgumentException("Cannot reflectively create enum objects");ConstructorAccessor ca = constructorAccessor;   // read volatileif (ca == null) {ca = acquireConstructorAccessor();}@SuppressWarnings("unchecked")T inst = (T) ca.newInstance(initargs);return inst;}

----------------------------BeanUtils.instantiateClass()-----------------------------------------------------------------------------------------------------------------------

ContextLoader.java中的determineContextClass()方法:

    /*** 返回上下文类型*/protected Class<?> determineContextClass(ServletContext servletContext) {//从servlet上下文中获取初始化配置参数contextClass的值String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);// 如果contextClassName不为null则放回配置的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 {// 如果没有配置则使用XmlWebApplicationContext,这个代码就是从contextLoad.properties配置中加载进来的,配置中的就是XmlWebApplicationContextcontextClassName = 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);}}}

在Spring web项目中XmlWebApplicationContext是如何创建的?

首先在web.xml中我们可以看到如下配置:

    <context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:META-INF/spring/*.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>

ContextLoaderListener继承Spring的ContextLoader上下文加载器类,同时实现ServletContextListener接口(Servlet上下文监听器),监听Web服务器上下文的启动和停止事件,管理Web环境中Spring的启动和销毁过程,

首先我们看看这个监听器的源码。初始化的入口是contextInitialized方法,它只是简单地将初始化功能委托为了ContextLoader进行处理。

org.springframework.web.context.ContextLoaderListener.java

    /*** Initialize the root web application context.初始化根WEB应用上下文*/@Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext()); //调用ContextLoader的initWebApplicationContext()}

通过对ContextLoaderListener的源码分析,我们看到ContextLoaderListener继承ContextLoader,所以ContextLoaderListener本身也是Spring的上下文加载器。

ContextLoaderListener实现了ServletContextListener接口,当Web应用在Web服务器中被被启动和停止时,Web服务器启动和停止事件会分别触发ContextLoaderListener的contextInitialized和contextDestroyed方法来初始化和销毁Spring上下文。我们通过上述对ContextLoaderListener的源码分析看到真正实现Spring上下文的初始化和销毁功能的是ContextLoader类,分析ContextLoader初始化和销毁Spring Web上下文的过程见上面。

 ContextLoader的initWebApplicationContext()的源码见上面的分析。

在springmvc中,如何实例化XmlWebApplicationContext的?
1、springmvc加载配置文件

<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:springmvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping>

org.springframework.web.servlet.DispatcherServlet是通过这个servlet,加载配置文件
FrameworkServlet中有一个属性

public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;

接着看FrameworkServlet的initWebApplicationContext()方法:

protected WebApplicationContext initWebApplicationContext() {
.....if (wac == null) {// No context instance is defined for this servlet -> create a local onewac = createWebApplicationContext(rootContext);}
}

 方法中有一个FrameworkServlet.createWebApplicationContext(rootContext)方法

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {//这个方法就是创建XmlWebApplicationContext实例的ClassClass<?> contextClass = getContextClass();if (this.logger.isDebugEnabled()) {this.logger.debug("Servlet with name '" + getServletName() +"' will try to create custom WebApplicationContext context of class '" +contextClass.getName() + "'" + ", using parent context [" + parent + "]");}
//如果该上下文类没有实现ConfigurableWebApplicationContext接口则抛出异常
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name '" + getServletName() +"': custom WebApplicationContext class [" + contextClass.getName() +"] is not of type ConfigurableWebApplicationContext");}
//调用BeanUtils.instantiateClass(contextClass),通过反射,调用XmlWebApplicationContext的无参构造函数实例化XmlWebApplicationContext对象ConfigurableWebApplicationContext wac
=(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());wac.setParent(parent);wac.setConfigLocation(getContextConfigLocation());configureAndRefreshWebApplicationContext(wac);return wac; }

接口看getContextClass()方法:

Class<?> contextClass = getContextClass();这个方法就是创建XmlWebApplicationContext实例的Class

    public Class<?> getContextClass() {return this.contextClass;}

 this.contextClass就是前面FrameworkServlet定义的全局变量。

至此,实例化XmlWebApplicationContext的步骤基本相同:

1、通过读取配置文件方式,读取到org.springframework.web.context.WebApplicationContext的类型为“org.springframework.web.context.support.XmlWebApplicationContext”;

2、检查上下文类没有实现ConfigurableWebApplicationContext接口则抛出异常;

3、调用BeanUtils.instantiateClass(contextClass),通过反射,调用XmlWebApplicationContext的无参构造函数实例化XmlWebApplicationContext对象;

二、XmlWebApplicationContext源码

ContextLoader初始化Spring Web上下文的determineContextClass方法中,我们知道Spring首先通过Servlet上下文从web.xml文件中获取用户自定义配置的contextClass参数值,如果没有获取到,则默认使用Spring的XmlWebApplicationContext作为Spring Web应用的IoC容器,XmlWebApplicationContext是WebApplicationContext的实现类ConfigurableWebApplicationContext的子类

public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {//Web应用中Spring配置文件的默认位置和名称,如果没有特别指定,则Spring会根据//此位置定义Spring Bean定义资源public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";//Spring Bean定义资源默认前缀public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";//Spring Bean定义资源默认后置public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";//在分析Spring IoC初始化过程中我们已经分析过,加载Spring Bean定义资源的方法,//通过Spring容器刷新的refresh()方法触发protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {//为Spring容器创建XML Bean定义读取器,加载Spring Bean定义资源XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// resource loading environment.
        beanDefinitionReader.setEnvironment(getEnvironment());//设置Bean定义读取器,因为XmlWebApplicationContext是DefaultResourceLoader的子类,所以使用默认资源加载器来定义Bean定义资源beanDefinitionReader.setResourceLoader(this);//为Bean定义读取器设置SAX实体解析器beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));//在加载Bean定义之前,调用子类提供的一些用户自定义初始化Bean定义读取器的方法
        initBeanDefinitionReader(beanDefinitionReader);//使用Bean定义读取器加载Bean定义资源
        loadBeanDefinitions(beanDefinitionReader);}//用户自定义初始化Bean定义读取器的方法protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {}//加载Bean定义资源protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {//获取定位的Bean定义资源路径String[] configLocations = getConfigLocations();if (configLocations != null) {//遍历加载所有定义的Bean定义资源for (String configLocation : configLocations) {reader.loadBeanDefinitions(configLocation);}}}//获取默认Bean定义资源protected String[] getDefaultConfigLocations() {//获取web.xml中的命名空间,如命名空间不为null,则返回 “/WEB-INF/命名空间.xml”if (getNamespace() != null) {return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};}//如果命名空间为null,则返回"/WEB-INF/applicationContext.xml"else {return new String[] {DEFAULT_CONFIG_LOCATION};}}
}

XmlWebApplicationContext将Web应用中配置的Spring Bean定义资源文件载入到Spring IoC容器中后,接下来的Spring IoC容器初始化和依赖注入的过程后面再分析。


 

转载于:https://www.cnblogs.com/duanxz/p/3507449.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408201.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++17代码整洁之道

C17代码整洁之道 C代码整洁的基本规范 良好的命名 名称应该自解释使用域中的名称避免使用匈牙利命名法(即名称前加类型)不要通过注释禁用代码 函数 只做一件事情 函数体量要小 当你为函数找到一个富有表现力的名称时,名称中没有连词 圈复杂度低 函数的参数要少 函数尽…

使用.NET Remoting开发分布式应用——基于租约的生存期(转载)

使用.NET Remoting开发分布式应用——基于租约的生存期一&#xff0e;概述知名类型的SingleCall对象可以在客户程序的方法调用之后被垃圾收集器清理掉&#xff0c;因为它没有保持状态&#xff0c;属于无状态的。而客户激活的类型的对象和知名类型的SingleTon对象都属于生存期长…

配置 Sybase数据源

1.Start-- > All Programs -- > Sybase - -> Connectivity --> click ‘Open Client Directory Service Editor’ 2.在Server窗口右击鼠标 Right Click -- > select ‘Add’ item 3 弹出’Input Server Name’窗口,输入Server name(服务器名称) ServerName: DM…

C++虚函数表

虚函数 对C 了解的人都应该知道虚函数&#xff08;Virtual Function&#xff09;是通过一张虚函数表&#xff08;Virtual Table&#xff09;来实现的。简称为V-Table。在这个表中&#xff0c;主是要一个类的虚函数的地址表&#xff0c;这张表解决了继承、覆盖的问题&#xff0…

Proxy Pattern using C# (转载)

Proxy Pattern&#xff08;代理模式&#xff09;属于Structural Pattern&#xff08;结构型模式&#xff09;&#xff0c;Proxy Pattern-为Client真正要调用的对象提供一个代理&#xff08;Surrogate or placeholder&#xff09;&#xff0c;来控制Client对该对象的访问。 1. U…

BlackBerry 应用程序开发者指南 第一卷:基础--第5章 支持的媒体内容(Media Content)...

作者:Confach 发表于April 23,2006 15:02 pm版权信息:可以任意转载, 转载时请务必以超链接形式标明文章原始出处 和作者信息.http://www.cnblogs.com/confach/articles/387902.html5第5章 支持的媒体内容&#xff08;Media Content&#xff09;PME内容 播放媒体内容 监听媒体内…

Qt 入门 ---- 如何在程序窗口显示图片?

步骤&#xff1a; 1. 选择资源&#xff08;准备图片&#xff09; 2. 加载资源&#xff08;导入图片&#xff09; 3. 使用资源&#xff08;显示图片&#xff09; 具体操作流程&#xff1a; ① 从网上寻找合适的图片素材&#xff0c;下载到本地&#xff0c;在项目根目录下创建一个…

Enterprise Library 2.0 技巧(3):记录ASP.NET站点中未处理的异常

这篇文章不能算是Enterprise Library 2.0的一个技巧&#xff0c;只是Logging Application Block的一个简单应用而已&#xff0c;在这里我们使用Logging Application Block来记录一个ASP.NET 2.0站点中未处理的异常到数据库中&#xff0c;当然你也可以记录到文本文件中&#xff…

C++自定义对象如何支持Range-based循环语法

自定义对象如何支持Range-based循环语法 至少实现以下两种语法: //返回第一个迭代子的位置 Iterator begin() //返回最后一个迭代子的下一个位置 Iterator end()迭代子需要支持如下三种方法: operator(自增)operator! (判不等)operator* (解引用) #include <iostream>…

SharePoint 2013 本地开发解决方案以及程调试

SharePoint 2013 本地开发解决方案以及程调试 在SharePoint开发中&#xff0c;我们需要在部署有SharePoint环境的服务器中开发&#xff0c;这是一件让人很苦恼的事情&#xff0c;毕竟不能一个项目多人开发配备多台服务器&#xff0c;这就需要本地开发。 本来自己以为SharePoint…

Linux与C++11多线程编程(学习笔记)

多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #include <unistd.h> #include <stdio.h> #include <pthread.h> void* threadfunc(…