DispatcherServlet请求流程解析-initWebA

作者: Real_man | 来源:发表于2018-08-19 15:23 被阅读29次

    上面一篇文章提到,在Servlet初始化的时候,获取属性后调用initServletBean,这个方法会initWebApplicationContext,这是DispatcherServlet对后面的处理做了很多的预先准备工作,我们一起来看看它到底做了什么事情。

    流程

    1. 先上代码,初始化和发布WebApplicationContext到servlet中,对于真正的创建操作是交给子类的createWebApplicationContext来实现的。
    /**
         * Initialize and publish the WebApplicationContext for this servlet.
         * <p>Delegates to {@link #createWebApplicationContext} for actual creation
         * of the context. Can be overridden in subclasses.
         * @return the WebApplicationContext instance
         * @see #FrameworkServlet(WebApplicationContext)
         * @see #setContextClass
         * @see #setContextConfigLocation
         */
        protected WebApplicationContext initWebApplicationContext() {
    // 获取WebApplicationContext,但是首次取得的值一般为null
            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
                            cwac.setParent(rootContext);
                        }
                        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
                // 在当前的servlet中,没有对应的context实例时候,创建一个。
                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);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                            "' as ServletContext attribute with name [" + attrName + "]");
                }
            }
    
            return wac;
        }
    
    • 首先获取WebApplicationContext,一般获取的值为null
    • 然后试着findWebApplicationContext(),这一步主要看其父类的是否已经注册了对应的context. 父类的context首次也为null
    • 如果WebApplicationContext还未找到,那么尝试创建一个WebApplicationContext
    image.png
    1. 上一步对于最初的操作,获得的WebapplicationContext都是null的,因此需要创建一个WebapplicationContext.

      为当前的Servlet实例化WebApplicationContext

    /**
         * Instantiate the WebApplicationContext for this servlet, either a default
         * {@link org.springframework.web.context.support.XmlWebApplicationContext}
         * or a {@link #setContextClass custom context class}, if set.
         * <p>This implementation expects custom contexts to implement the
         * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
         * interface. Can be overridden in subclasses.
         * <p>Do not forget to register this servlet instance as application listener on the
         * created context (for triggering its {@link #onRefresh callback}, and to call
         * {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
         * before returning the context instance.
         * @param parent the parent ApplicationContext to use, or {@code null} if none
         * @return the WebApplicationContext for this servlet
         * @see org.springframework.web.context.support.XmlWebApplicationContext
         */
        protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
           
           //获取默认的getContextClass,默认的contextClass为XmlWebApplicationContext.class,可以从源码中看到
            Class<?> 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 + "]");
            }
            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实例化ApplicationContext类。
            ConfigurableWebApplicationContext wac =
                    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    
            wac.setEnvironment(getEnvironment());
            wac.setParent(parent);
            wac.setConfigLocation(getContextConfigLocation());
    
            configureAndRefreshWebApplicationContext(wac);
    
            return wac;
        }
    
    
    • 首先获取默认的要实例化的ApplicationContxt类,默认的为XmlWebApplicationContext
    • 使用BeanUtil实例化ApplicationContxt的类,BeanUtils是个很有用的方法,可以多看下
    • ApplicationContxt被实例化之后,做一些配置,设置当前的Environment,设置父类(如果有的话),获取contextLocation的位置(web.xml中可以设置,当然也有默认值)
    • 配置然后刷新WebApplicationContex类
    1. 对WebApplicationContext类做配置和刷新。
    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();
        }
    
    • 首先生成WebApplicationContext的id,用于后面加载Spring-MVC的配置文件
    • 在WebApplicationContext中设置容器的ServletContext,ServiletConfig,并设置默认的命名空间
    • 添加ApplicationContext的监听器,监听ContextRefresh事件
    • 初始化环境属性
    • postProcessWebApplicationContext,初始化WebApplicationContext之后做的事情,默认为空,后面自行扩展的时候可以覆盖
    • applyInitializers, 后面可以多了解下,初始器
    • 刷新WebApplicationContext.
    image.png image.png
    1. WebApplicationContext的刷新操作。
    • 使用synchronized保证线程安全
    • 下面每一个方法都包含一些具体的操作
    @Override
        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                // 准备刷新
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                // 告诉子类刷新内部的bean factory
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                // 准备beanfactory来使用
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
    
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
    
                    // Initialize message source for this context.
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    finishRefresh();
                }
    
                catch (BeansException ex) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context initialization - " +
                                "cancelling refresh attempt: " + ex);
                    }
    
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
    
                finally {
                    // Reset common introspection caches in Spring's core, since we
                    // might not ever need metadata for singleton beans anymore...
                    resetCommonCaches();
                }
            }
        }
    
    
    1. 仔细看一下refresh中的每一个大方法,首先prepareRefresh,可以看到prepareRefresh,确实只做一些准备性的工作,没有实质性的处理。
    protected void prepareRefresh() {
            this.startupDate = System.currentTimeMillis();
            this.closed.set(false);
            this.active.set(true);
    
            if (logger.isInfoEnabled()) {
                logger.info("Refreshing " + this);
            }
    
            // Initialize any placeholder property sources in the context environment
            initPropertySources();
    
            // Validate that all properties marked as required are resolvable
            // see ConfigurablePropertyResolver#setRequiredProperties
            getEnvironment().validateRequiredProperties();
    
            // Allow for the collection of early ApplicationEvents,
            // to be published once the multicaster is available...
            this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
        }
    
    1. obtainFreshBeanFactory()告诉子类刷新内部的beanfactory, 默认的beanFactory是DefaultListableBeanFactory。
    image.png

    obtainFreshBeanFactory中刷新BeanFactory的操作是交给其子类来实现的。AbstractRefreshableApplicationContext是真正做了刷新的操作,并且加载了beanDefinitions.

    关于如何loadBeandefenition,后面可以再深入去看

    
    /**
         * Tell the subclass to refresh the internal bean factory.
         * @return the fresh BeanFactory instance
         * @see #refreshBeanFactory()
         * @see #getBeanFactory()
         */
        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            refreshBeanFactory();
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            if (logger.isDebugEnabled()) {
                logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
            }
            return beanFactory;
        }
        
    
    /**
         * This implementation performs an actual refresh of this context's underlying
         * bean factory, shutting down the previous bean factory (if any) and
         * initializing a fresh bean factory for the next phase of the context's lifecycle.
         */
        @Override
        protected final void refreshBeanFactory() throws BeansException {
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                beanFactory.setSerializationId(getId());
                customizeBeanFactory(beanFactory);
                
                //加载bean的定义
                loadBeanDefinitions(beanFactory);
                synchronized (this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            }
            catch (IOException ex) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
            }
        }
    
    1. 让beanFactory准备好使用context. 可以看到beanFactory做了很多的配置
    • 设置ClasLoader,SPEL,PropertyEditorRegistrar
    • 添加ApplicationContextAware,用于让bean设置ApplicationContextde .等等
    • 解析依赖关系ResolvableDependency相关的内容
    • 一些需要早期的bean处理
    • 注册一些系统默认的bean,如Environment,SystemProperties等等
    /**
         * Configure the factory's standard context characteristics,
         * such as the context's ClassLoader and post-processors.
         * @param beanFactory the BeanFactory to configure
         */
        protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            // Tell the internal bean factory to use the context's class loader etc.
            beanFactory.setBeanClassLoader(getClassLoader());
            beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
            beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
            // Configure the bean factory with context callbacks.
            beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
            beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
            beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
            beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
            beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
            beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
            beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    
            // BeanFactory interface not registered as resolvable type in a plain factory.
            // MessageSource registered (and found for autowiring) as a bean.
            beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
            beanFactory.registerResolvableDependency(ResourceLoader.class, this);
            beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
            beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
            // Register early post-processor for detecting inner beans as ApplicationListeners.
            beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    
            // Detect a LoadTimeWeaver and prepare for weaving, if found.
            if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
                beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
                // Set a temporary ClassLoader for type matching.
                beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
            }
    
            // Register default environment beans.
            if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
                beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
            }
            if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
                beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
            }
            if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
                beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
            }
        }
    
    1. beanFactory处理之后的处理,这一步可以理解为上一步的后续处理,和上一步类似都是对beanFactory的处理
    /**
         * Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
         */
        @Override
        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
            beanFactory.ignoreDependencyInterface(ServletContextAware.class);
            beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
    
            WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
            WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
        }
    
    
    1. 激发上下文中创建bean的后续操作,这个内部包含了很多内容,大部分的操作在PostProcessorRegistrationDelegate类里面的invokeBeanFactoryPostProcessors中,一个方法写了100多行,内容很多,这里就粘贴出来了,东西太多。记得PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors

    包括下一步的registerBeanPostProcessors操作也是由PostProcessorRegistrationDelegate来完成的。这个类看来也挺重要,不过它的访问权限是默认的,看来Spring不想暴露出来给我们用。

    PostProcessorRegistrationDelegate

    1. initMessageSource,操作,进入里面看到这些操作都是围绕着BeanFactory在进行,beanFactory设置DelegatingMessageSource。


      image.png
    1. 设置Application广播发出器,可以看到内部仍然是对beanFactory设置一个默认的类


      image.png
    1. onRefresh方法其实现类基本上都是在初始化主题,GenericWebApplicationContext与AbstractRefreshableWebApplicationContext都是如此


      image.png
    1. 注册监听器,看截图可以看出其主要在上面的事件caster中添加应用事件的bean
    image.png
    1. 看一下最后的操作吧,结束刷新操作,发布已经刷新后的事件。
        /**
         * Finish the refresh of this context, invoking the LifecycleProcessor's
         * onRefresh() method and publishing the
         * {@link org.springframework.context.event.ContextRefreshedEvent}.
         */
        protected void finishRefresh() {
            // Initialize lifecycle processor for this context.
            initLifecycleProcessor();
    
            // Propagate refresh to lifecycle processor first.
            getLifecycleProcessor().onRefresh();
    
            // Publish the final event.
            publishEvent(new ContextRefreshedEvent(this));
    
            // Participate in LiveBeansView MBean, if active.
            LiveBeansView.registerApplicationContext(this);
        }
    
    1. 重置Spring中的cache
        /**
         * Reset Spring's common core caches, in particular the {@link ReflectionUtils},
         * {@link ResolvableType} and {@link CachedIntrospectionResults} caches.
         * @since 4.2
         * @see ReflectionUtils#clearCache()
         * @see ResolvableType#clearCache()
         * @see CachedIntrospectionResults#clearClassLoader(ClassLoader)
         */
        protected void resetCommonCaches() {
            ReflectionUtils.clearCache();
            ResolvableType.clearCache();
            CachedIntrospectionResults.clearClassLoader(getClassLoader());
        }
    
    

    回顾

    感觉Spring-mvc中的initWebApplicationContext做了很多的事情,先抓住大方向,细节很多,回顾一下这次看到了那些内容。

    从initServletBean开始,然后initWebApplicationContext,初始情况下ApplicationContext是为null的,我们需要创建默认的ApplicationContext,Spring-mvc中默认的ApplicationContext是XmlWebApplicationContext。

    使用反射创建XmlWebApplicationContext之后,将XmlWebApplicationContext作为参数传递到配置和刷新XmlWebApplicationContext的方法中,接下来的操作,集中在对XmlWebApplicationContext的配置中。

    配置Enviriment,ServletConfig,初始属性,命名空间,过滤器等等。最后调用XmlWebApplicationContext的refresh操作。

    refresh操作中主要围绕bean相关的内容在操作,创建beanFactory,然后对beanFactory做配置,同时加载bean,bean的生命周期等等操作。然后注册bean以外的内容,配置主题,事件,消息等等,最后结束刷新,完成整个bean的生命周期。

    最后

    这里只是initWebApplicationContext,感觉bean的生命周期处理也是在这一步内部配置的,后面有关bean的生命周期,可以在内部仔细看一下。

    initWebApplicationContext中,涉及到很多的类,很多默认的类,多读几遍,也许将来自己造轮子的某一天可以参考Spring的设计。

    本人才疏学浅,阅读Spring源码的过程,做了一些记录,供参考作用,如果感兴趣,想后续交流欢迎一起探讨。

    相关文章

      网友评论

        本文标题:DispatcherServlet请求流程解析-initWebA

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