zoukankan      html  css  js  c++  java
  • 再看SpringMVC通过一个DispatcherServlet处理Servlet

    初始入口:

    org.springframework.web.context.ContextLoaderListener
    
    org.springframework.web.context.ContextLoader

    类的继承关系图如下:

    可以看到这个Listener实现了Servlet标准提供的接口,这个listener只有两个方法是 用来监听web容器启动和关闭动作的:

        /**
         * Receives notification that the web application initialization
         * process is starting.
         *
         * <p>All ServletContextListeners are notified of context   // 这里已经说了,使用的观察者模式,容器启动会调用
         * initialization before any filters or servlets in the web
         * application are initialized.
         *
         * @param sce the ServletContextEvent containing the ServletContext
         * that is being initialized
         */
        public void contextInitialized(ServletContextEvent sce);
    
        /**
         * Receives notification that the ServletContext is about to be
         * shut down.
         *
         * <p>All servlets and filters will have been destroyed before any
         * ServletContextListeners are notified of context
         * destruction.
         *
         * @param sce the ServletContextEvent containing the ServletContext  //这里也说了,容器关闭会调用 
         * that is being destroyed
         */
        public void contextDestroyed(ServletContextEvent sce);

    当然ContextLoaderListener实现了这两个方法:

        /**
         * Initialize the root web application context.
         */
        @Override
        public void contextInitialized(ServletContextEvent event) {
            initWebApplicationContext(event.getServletContext());  //这个方法在父类ContextLoader中实现 ,这里只是把ServletContext当参数传过去了,因为在执行逻辑中需要用到很多与web容器相关的参数
        }
    
    
        /**
         * Close the root web application context.
         */
        @Override
        public void contextDestroyed(ServletContextEvent event) {
            closeWebApplicationContext(event.getServletContext());
            ContextCleanupListener.cleanupAttributes(event.getServletContext());  //同上
        }

    切换到父类ContextLoader 中initWebApplicationContext方法中:

    /**
         * Initialize Spring's web application context for the given servlet context,
         * using the application context provided at construction time, or creating a new one
         * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
         * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
         * @param servletContext current servlet context
         * @return the new WebApplicationContext
         * @see #ContextLoader(WebApplicationContext)
         * @see #CONTEXT_CLASS_PARAM
         * @see #CONFIG_LOCATION_PARAM
         */
        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!");
            }
    
            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) {
                    this.context = createWebApplicationContext(servletContext);                   //这里先创建Web环境的 ApplicationContext ,这个Context的地位就类似于ServletContext,是获取Bean的入口
                }
                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
                        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);          //这才是最核心的方法,看名字就知道 配置并刷新 ApplicationContext,这个动作完成了类示例的创建,初始化和 注册事件的发布,各种监听的执行, 为
                    }                                                                            //DispatcherServlet这个核心类执行Handler的注册做好了万全准备
                }
                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);                //这里可以看到把当前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;
            }
        }

    先来看看ApplicationContext的创建,进入createWebApplicationContext,毕竟它是获取Bean的入口。

    /**
         * Instantiate the root WebApplicationContext for this loader, either the
         * default context class or a custom context class if specified.
         * <p>This implementation expects custom contexts to implement the
         * {@link ConfigurableWebApplicationContext} interface.
         * Can be overridden in subclasses.
         * <p>In addition, {@link #customizeContext} gets called prior to refreshing the
         * context, allowing subclasses to perform custom modifications to the context.
         * @param sc current servlet context
         * @return the root WebApplicationContext
         * @see ConfigurableWebApplicationContext
         */
        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);
        }
    
        /**
         * Return the WebApplicationContext implementation class to use, either the
         * default XmlWebApplicationContext or a custom context class if specified.
         * @param servletContext current servlet context
         * @return the WebApplicationContext implementation class to use
         * @see #CONTEXT_CLASS_PARAM
         * @see org.springframework.web.context.support.XmlWebApplicationContext
         */
        protected Class<?> determineContextClass(ServletContext servletContext) {
            String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);   
            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);
                }
            }
        }

    这里先从ServletContext中获取参数是contextClass的值,也就是从web.xml读取contextParam或者硬编码的方式(servlet3.0以上的版本)中获取配置指定的ApplicationContext。如果没有,使用默认的策略 XmlWebApplicationContext,这个默认的策略 defaultStrategies是在ContextLoader开头的静态代码块中初始的:

    /**
         * Name of the class path resource (relative to the ContextLoader class)
         * that defines ContextLoader's default strategy names.
         */
        private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";      
    
    
        private static final Properties defaultStrategies;
    
        static {
            // Load default strategy implementations from properties file.
            // This is currently strictly internal and not meant to be customized
            // by application developers.
            try {
                ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);  //这里从ContextLoader所在的包下加载一个叫ContextLoader.properties的文件,
    //这个文件在spring-web包的context子包中,内容只有一行,配置了默认的WebApplicationContext defaultStrategies
    = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); } }

    有了ApplicationContext了,再回到上面initWebApplicationContext方法,下面就开始判断这个context是不是ConfigurableWebApplicationContext实例,如果是,并且没有执行过刷新,就执行配置刷新的方法,也就是上面提到的最核心的方法,用于bean的初始化,

    这里在判断后,还获取了parent context并将它设置成当前context的parent.

    当然最核心的入口,一切源头:

    org.springframework.web.servlet.DispatcherServlet

    该类的继承关系(使用Idea分析工具)如下:

    DefaultListableBeanFactory类的继承关系:

    与Bean创建时相关的Aware接口的三个直接子接口:

  • 相关阅读:
    LeetCode 32. 最长有效括号(Longest Valid Parentheses)
    LeetCode 141. 环形链表(Linked List Cycle)
    LeetCode 160. 相交链表(Intersection of Two Linked Lists)
    LeetCode 112. 路径总和(Path Sum)
    LeetCode 124. 二叉树中的最大路径和(Binary Tree Maximum Path Sum)
    LightGBM新特性总结
    sql service 事务与锁
    C#泛型实例详解
    C# 中的委托和事件(详解)
    C# DateTime日期格式化
  • 原文地址:https://www.cnblogs.com/blentle/p/7815787.html
Copyright © 2011-2022 走看看