• Servlet容器初始化IOC容器


    <!-- ServletContext参数,配置Ioc容器的xml文件名 -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
        
    <!-- 初始化Ioc容器的监听器(重点就是这个监听器)-->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>

    看下这个监听器:

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
     
        public ContextLoaderListener() {
        }
        public ContextLoaderListener(WebApplicationContext context) {
            super(context);
        }
        @Override
        public void contextInitialized(ServletContextEvent event) {
            initWebApplicationContext(event.getServletContext());
        }
     
        public void contextDestroyed(ServletContextEvent event) {
            closeWebApplicationContext(event.getServletContext());
            ContextCleanupListener.cleanupAttributes(event.getServletContext());
        }
     
    }

    这个监听器首先实现了ServletContextListener,所以会在Servlet容器初始化和销毁的时候分别调用contextInitialized( )与contextDestroyed( )方法。

    注意这里的带参构造方法 ContextLoaderListener(WebApplicationContext context)是调用了父类ContextLoader的构造方法,并传参进去。

    而ContextLoader的主要作用就是初始化Ioc容器,下面我们来看看ContextLoader这个类

    public class ContextLoader {
    public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";//ServletContext上下文参数
    private WebApplicationContext context;
    public ContextLoader(WebApplicationContext context) { this.context = context; }
    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!"); }//不能初始化Ioc容器,因为已经有一个根Application上下文,检查在web.xml中是否有多个ContextLoader Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); }//开始初始化WebApplicationContext long startTime = System.currentTimeMillis();//当前毫秒数 try { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) {//Ioc容器并未初始化,所以暂时不能提供服务 // 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) {//设置父applicationContext // 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); } } 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; } }

    这里是总过程的描述:

    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
            Class<?> contextClass = determineContextClass(sc);//获取context是由那个类定义的
            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);
        }

    通过determineContextClass( )知道了实例化IOC容器方式,然后用这个方式,再通过反射机制实例化容器,即实例化webApplicationContext。

    protected Class<?> determineContextClass(ServletContext servletContext) {
            String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);//该属性为"ContextClass"
            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 {
                contextClassName = 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);
                }
            }
        }

    因为实例化Ioc容器的方式有几种,而这个方法是判定需要实例化那种类来实例化当前Web容器的Spring Ioc根容器。要判定需要实例化哪种类来实例化当前web容器的Spring根容器,如果我们设置了名称为“contextClass”的context-param,则取我们设置的类,该类应当实现ConfigurableWebApplicationContext接口或继承自实现了该接口的子类(如XmlWebApplicationContext、GroovyWebApplicationContext和AnnotationConfigWebApplicationContext),通常我们都不会设置,Spring会默认取与ContextLoader同目录下的ContextLoader.properties中记录的类名作为根容器的类型(默认是org.springframework.web.context.support.XmlWebApplicationContext);

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
            if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
                // The application context id is still set to its original default value
                // -> assign a more useful id based on available information
                String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
                if (idParam != null) {
                    wac.setId(idParam);
                }
                else {
                    // Generate default id...
                    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);
            }
     
            // The wac environment's #initPropertySources will be called in any case when the context
            // is refreshed; do it eagerly here to ensure servlet property sources are in place for
            // use in any post-processing or initialization that occurs below prior to #refresh
            ConfigurableEnvironment env = wac.getEnvironment();
            if (env instanceof ConfigurableWebEnvironment) {
                ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
            }
     
            customizeContext(sc, wac);
            wac.refresh();
    
    }

    虽然实例化了IOC容器,但是并未完成IOC容器的初始化,还不能提供服务。该方法的逻辑主要有一下几点:设置一个contextId(从contextId这个param获取,如果没有则默认是WebApplicationContext的类名 + “:” + servlet context的路径);设置配置位置(从contextConfigLocation 这个param获取,如果未配置,则默认是/WEB-INF/applicationContext.xml,在XmlWebApplicationContext中可以看出);自定义该congtext;调用该Context的refresh()方法。

    上面描述了WebApplicationContext的构造,默认还是使用反射的方式构建XmlWebApplicationContext实例

    public void closeWebApplicationContext(ServletContext servletContext) {
            servletContext.log("Closing Spring root WebApplicationContext");
            try {
                if (this.context instanceof ConfigurableWebApplicationContext) {
                    ((ConfigurableWebApplicationContext) this.context).close();
                }
            }
            finally {
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
                if (ccl == ContextLoader.class.getClassLoader()) {
                    currentContext = null;
                }
                else if (ccl != null) {
                    currentContextPerThread.remove(ccl);
                }
                servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
                if (this.parentContextRef != null) {
                    this.parentContextRef.release();
                }
            }
        }

    该方法完成了对Ioc容器的销毁。

    PS:webApplicationContext实现了ApplicationContext接口的子类。是专门为WEB应用准备的。

    1. 它允许从相对于Web根目录的路径中加载配置文件完成初始化工作。从WebApplicationContext中可以获取ServletContext引用,整个Web应用上下文对象将作为属性放置在ServletContext中,以便Web应用环境可以访问Spring上下文。 
    2.WebApplicationContext还为Bean提供了三个新的作用域,request、session和globalsession。 
    其中两个参数HttpServletRequest:服务器从客户端拿去数据 
    HttpServletResponse:服务器向前台传送数据



    原文:https://blog.csdn.net/Wayne_y/article/details/79778207

  • 相关阅读:
    B-Suffix Array
    1 or 2
    Boundary
    Fake Maxpooling
    Cover the Tree
    Omkar and Circle
    20.5.31
    Yet Another Yet Another Task
    Codeforces Round #373 (Div. 2)E. Sasha and Array +线段树+矩阵快速幂
    2018 Multi-University Training Contest 2(Naive Operations ) hdu6315
  • 原文地址:https://www.cnblogs.com/toov5/p/10614482.html
Copyright © 2020-2023  润新知