美文网首页
spring mvc 父子容器

spring mvc 父子容器

作者: 有点胖的小乐乐 | 来源:发表于2018-11-18 21:33 被阅读39次

    看本文之前首先需了解 spring注解驱动开发。可以看另外一篇文章spring注解,本文是在spring注解开发的基础上深入研究。

    spring 本身的容器

    在AbstractContextLoaderInitializer类中有如下代码:

    AbstractContextLoaderInitializer 作用可以看spring注解

    protected void registerContextLoaderListener(ServletContext servletContext) {
    // 创建父容器        
    WebApplicationContext rootAppContext = createRootApplicationContext();
            if (rootAppContext != null) {
                ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
                listener.setContextInitializers(getRootApplicationContextInitializers());
                servletContext.addListener(listener);
            }
            else {
                logger.debug("No ContextLoaderListener registered, as " +
                        "createRootApplicationContext() did not return an application context");
            }
        }
    

    这个监听器。


    image.png
    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    
        public ContextLoaderListener() {
        }
        public ContextLoaderListener(WebApplicationContext context) {
            super(context);
        }
    
           // 容器启动完成的时候调用
        @Override
        public void contextInitialized(ServletContextEvent event) {
            initWebApplicationContext(event.getServletContext());
        }
    
            // 容器停止的时候调用
        @Override
        public void contextDestroyed(ServletContextEvent event) {
            closeWebApplicationContext(event.getServletContext());
            ContextCleanupListener.cleanupAttributes(event.getServletContext());
        }
    
    }
    
    public interface ServletContextListener extends EventListener {
        // 容器启动完成的时候调用
        public default void contextInitialized(ServletContextEvent sce) {
        }
        // 容器停止的时候调用
        public default void contextDestroyed(ServletContextEvent sce) {
        }
    
    }
    

    在创建完rootAppContext的时候,会在容器中注入一个监听器(ServletContextListener)。根据servl的规范,在容器启动时候调用contextInitialized方法,在容器停止的时候调用contextDestroyed。

    在容器启动完成的时候调用contextInitialized,进而调用initWebApplicationContext,完成父容器的初始化和refres整个过程。

    spring mvc 容器

    在AbstractDispatcherServletInitializer的类中有如下代码:

    AbstractDispatcherServletInitializer 作用可以看[spring注解]

    
    @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);
            registerDispatcherServlet(servletContext);
        }
    
        protected void registerDispatcherServlet(ServletContext servletContext) {
            String servletName = getServletName();
            Assert.hasLength(servletName, "getServletName() must not return null or empty");
    
    //  创建web 子容器
            WebApplicationContext servletAppContext = createServletApplicationContext();
            Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
    
     // 将web子容器 注入到FrameworkServlet
            FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
            Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
            dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
    
            ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
            if (registration == null) {
                throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
                        "Check if there is another servlet registered under the same name.");
            }
                     // 配置立即生效
            registration.setLoadOnStartup(1);
            registration.addMapping(getServletMappings());
            registration.setAsyncSupported(isAsyncSupported());
    
            Filter[] filters = getServletFilters();
            if (!ObjectUtils.isEmpty(filters)) {
                for (Filter filter : filters) {
                    registerServletFilter(servletContext, filter);
                }
            }
    
            customizeRegistration(registration);
        }
    

    在registerDispatcherServlet方法中,首先创建FrameworkServlet对象实际上创建的是DispatcherServlet对象。

    protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
            return new DispatcherServlet(servletAppContext);
        }
    

    DispatcherServlet的继承关系如下


    DispatcherServlet

    根据Servlet的规范。servlet初始化的时候会调用servlet init方法。
    通过查看spring可以看到以下调用关系。

    HttpServletBean.init()->HttpServletBean.initServletBean()->FrameworkServlet重写initServletBean 。
    FrameworkServlet的代码initServletBean

        @Override
        protected final void initServletBean() throws ServletException {
            getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
            if (logger.isInfoEnabled()) {
                logger.info("Initializing Servlet '" + getServletName() + "'");
            }
            long startTime = System.currentTimeMillis();
    
            try {
                        // 初始化web自容器。
                this.webApplicationContext = initWebApplicationContext();
                initFrameworkServlet();
            }
            catch (ServletException | RuntimeException ex) {
                logger.error("Context initialization failed", ex);
                throw ex;
            }
    
            if (logger.isDebugEnabled()) {
                String value = this.enableLoggingRequestDetails ?
                        "shown which may lead to unsafe logging of potentially sensitive data" :
                        "masked to prevent unsafe logging of potentially sensitive data";
                logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                        "': request parameters and headers will be " + value);
            }
    
            if (logger.isInfoEnabled()) {
                logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
            }
        }
    
    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
                                   // 设置web子容器的父容器
                            cwac.setParent(rootContext);
                        }
                                   // 配置和refresh web子容器
                        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);
            }
    
            return wac;
        }
    
        protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
            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
                if (this.contextId != null) {
                    wac.setId(this.contextId);
                }
                else {
                    // Generate default id...
                    wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                            ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
                }
            }
    
            wac.setServletContext(getServletContext());
            wac.setServletConfig(getServletConfig());
            wac.setNamespace(getNamespace());
            wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
    
            // 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(getServletContext(), getServletConfig());
            }
    
            postProcessWebApplicationContext(wac);
            applyInitializers(wac);
            wac.refresh();
        }
    
        @Override
        protected void onRefresh(ApplicationContext context) {
            initStrategies(context);
        }
    
        /**
         * Initialize the strategy objects that this servlet uses.
         * <p>May be overridden in subclasses in order to initialize further strategy objects.
         */
        protected void initStrategies(ApplicationContext context) {
            initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            initHandlerMappings(context);
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            initViewResolvers(context);
            initFlashMapManager(context);
        }
    

    相关文章

      网友评论

          本文标题:spring mvc 父子容器

          本文链接:https://www.haomeiwen.com/subject/nzvgfqtx.html