当前位置: 移动技术网 > IT编程>开发语言>Java > Web容器初始化过程

Web容器初始化过程

2018年09月17日  | 移动技术网IT编程  | 我要评论

一、springmvc启动过程

spring的mvc是基于servlet功能实现的,每个web工程中都有一个web.xml文件,web容器在启动的时候会加载这个配置文件,当一个web应用加载到web容器中后,在web应用开始响应客户端的请求之前,要按照顺序执行下面几个步骤:

1、实例化部署描述符中的<listener>元素标识的时间监听器的实例;

<listener>
    <listener-class>org.springframework.web.context.contextloaderlistener</listener-class>
</listener>

2、在实现了servletcontextlistener接口的监听器实例中,调用contextinitialized()方法。contextloaderlistener实现了servletcontextlistener接口,servletcontextlistener作为servlet的监听者,会在servletcontext创建、销毁过程中监听servletcontextevent事件,然后进行响应的处理。换句话说,就是在启动web容器时,contextloaderlistener类会自动装配applicationcontext的配置信息。

public class contextloaderlistener extends contextloader implements servletcontextlistener {
    public contextloaderlistener() {
    }

    public contextloaderlistener(webapplicationcontext context) {
        super(context);
    }
  // 这里调用其父类contextloader的initwebapplicationcontext进行初始化工作   
    public void contextinitialized(servletcontextevent event) {
        this.initwebapplicationcontext(event.getservletcontext());
    }
  
    public void contextdestroyed(servletcontextevent event) {
        this.closewebapplicationcontext(event.getservletcontext());
        contextcleanuplistener.cleanupattributes(event.getservletcontext());
    }
}

  contextloaderlistener类继承了contextloader,后者是实际的servletcontextlistener接口实现者,这里用到了代理模式,利用contextloader类创建spring application context,并将其注册到servletcontext中去。contextloaderlistener就是调用contextloader类的initwebapplicationcontext()方法和closewebapplicationcontext()方法来实现servletcontext的创建和销毁工作的。

  

   其中,initwebapplicationcontext()方法主要完成了两个部分的工作:一是查看是否已经指定父容器,如果不存在则设置其父容器。二是将spring webapplicationcontext放置到线程map中。

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!");
        } else {
            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 {
                if (this.context == null) {
                    this.context = this.createwebapplicationcontext(servletcontext);
                }

                if (this.context instanceof configurablewebapplicationcontext) {
                    configurablewebapplicationcontext cwac = (configurablewebapplicationcontext)this.context;
                    if (!cwac.isactive()) {
              //查看是否指定父容器
                        if (cwac.getparent() == null) {
                            applicationcontext parent = this.loadparentcontext(servletcontext);
                            cwac.setparent(parent);
                        }

                        this.configureandrefreshwebapplicationcontext(cwac, servletcontext);
                    }
                }

                servletcontext.setattribute(webapplicationcontext.root_web_application_context_attribute, this.context);
                classloader ccl = thread.currentthread().getcontextclassloader();
                //将webapplicationcontext放置到线程map中
                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 var8) {
                logger.error("context initialization failed", var8);
                servletcontext.setattribute(webapplicationcontext.root_web_application_context_attribute, var8);
                throw var8;
            } catch (error var9) {
                logger.error("context initialization failed", var9);
                servletcontext.setattribute(webapplicationcontext.root_web_application_context_attribute, var9);
                throw var9;
            }
        }
    }    

在初始化方法执行后,将webapplicationcontext存到了两个地方,分别是servletcontext和currentcontextperthread中,我们可以通过这两个地方来获取webapplicationcontext:1.从servletcontext中去拿;2.从当前的线程map中去拿。

3、实例化部署符中<filter>元素标识的过滤器实例,并调用每个过滤器实例的init()方法。

4、实例化部署符中<servlet>元素标识的servlet实例,按照该元素包含的load-on-startup顺序,调用每个servlet实例的init()方法。

  <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class>

在springmvc的架构中,dispatcherservlet主要负责客户端请求的分发,起到控制器的作用。dispatcherservlet是实现servlet接口的实现类,其结构如下图所示。servlet的生命周期由servlet的容器来控制,主要分为初始化、运行和销毁三个阶段,分别对应三个方法:init()、service()和destory()。

  在dispatcherservlet的父类httpservletbean中,重写了init()方法,主要实现将当前的servlet类实例转换为beanwrapper类型实例,以便使用spring中提供的注入功能进行相应属性的注入。

  在dispatcherservlet的父类fameworkservelet中,重写了service()方法,对于不同的方法,spring统一将程序引导至processrequest(request,response)中,processrequest方法再对处理请求进行了一些准备工作后,又将细节实现转移到了doservice方法中。

  protected void service(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
        string method = request.getmethod();
        if (method.equalsignorecase(requestmethod.patch.name())) {
            this.processrequest(request, response);
        } else {
            super.service(request, response);
        }

   }
  protected final void processrequest(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
        long starttime = system.currenttimemillis();
        throwable failurecause = null;
        localecontext previouslocalecontext = localecontextholder.getlocalecontext();
        localecontext localecontext = this.buildlocalecontext(request);
        requestattributes previousattributes = requestcontextholder.getrequestattributes();
        servletrequestattributes requestattributes = this.buildrequestattributes(request, response, previousattributes);
        webasyncmanager asyncmanager = webasyncutils.getasyncmanager(request);
        asyncmanager.registercallableinterceptor(frameworkservlet.class.getname(), new frameworkservlet.requestbindinginterceptor());
        this.initcontextholders(request, localecontext, requestattributes);
        try {
            //调用dispatcherservlet的doservice()方法
            this.doservice(request, response);
        } catch (servletexception var17) {
            failurecause = var17;
            throw var17;
        } catch (ioexception var18) {
            failurecause = var18;
            throw var18;
        } catch (throwable var19) {
            failurecause = var19;
            throw new nestedservletexception("request processing failed", var19);
        } finally {
            this.resetcontextholders(request, previouslocalecontext, previousattributes);
            if (requestattributes != null) {
                requestattributes.requestcompleted();
            }
            if (this.logger.isdebugenabled()) {
                if (failurecause != null) {
                    this.logger.debug("could not complete request", (throwable)failurecause);
                } else if (asyncmanager.isconcurrenthandlingstarted()) {
                    this.logger.debug("leaving response open for concurrent processing");
                } else {
                    this.logger.debug("successfully completed request");
                }
            }
            this.publishrequesthandledevent(request, response, starttime, (throwable)failurecause);
        }
    }        
protected void doservice(httpservletrequest request, httpservletresponse response) throws exception {
        if (this.logger.isdebugenabled()) {
            string resumed = webasyncutils.getasyncmanager(request).hasconcurrentresult() ? " resumed" : "";
            this.logger.debug("dispatcherservlet with name '" + this.getservletname() + "'" + resumed + " processing " + request.getmethod() + " request for [" + getrequesturi(request) + "]");
        }

        map<string, object> attributessnapshot = null;
        if (webutils.isincluderequest(request)) {
            attributessnapshot = new hashmap();
            enumeration attrnames = request.getattributenames();
            label108:
            while(true) {
                string attrname;
                do {
                    if (!attrnames.hasmoreelements()) {
                        break label108;
                    }

                    attrname = (string)attrnames.nextelement();
                } while(!this.cleanupafterinclude && !attrname.startswith("org.springframework.web.servlet"));

                attributessnapshot.put(attrname, request.getattribute(attrname));
            }
        }
        request.setattribute(web_application_context_attribute, this.getwebapplicationcontext());
        request.setattribute(locale_resolver_attribute, this.localeresolver);
        request.setattribute(theme_resolver_attribute, this.themeresolver);
        request.setattribute(theme_source_attribute, this.getthemesource());
        flashmap inputflashmap = this.flashmapmanager.retrieveandupdate(request, response);
        if (inputflashmap != null) {
            request.setattribute(input_flash_map_attribute, collections.unmodifiablemap(inputflashmap));
        }

        request.setattribute(output_flash_map_attribute, new flashmap());
        request.setattribute(flash_map_manager_attribute, this.flashmapmanager);

        try {
           // 最终的请求处理函数
            this.dodispatch(request, response);
        } finally {
            if (!webasyncutils.getasyncmanager(request).isconcurrenthandlingstarted() && attributessnapshot != null) {
                this.restoreattributesafterinclude(request, attributessnapshot);
            }

        }

    }

  经过层层的准备工作,最终在dodispatch()方法中实现了完整的请求处理过程:

protected void dodispatch(httpservletrequest request, httpservletresponse response) throws exception {
        httpservletrequest processedrequest = request;
        handlerexecutionchain mappedhandler = null;
        boolean multipartrequestparsed = false;
        webasyncmanager asyncmanager = webasyncutils.getasyncmanager(request);

        try {
            try {
                modelandview mv = null;
                exception dispatchexception = null;

                try {
                    processedrequest = this.checkmultipart(request);
                    multipartrequestparsed = processedrequest != request;
                //根据request信息寻找对应的处理器
                    mappedhandler = this.gethandler(processedrequest);
                    if (mappedhandler == null || mappedhandler.gethandler() == null) {
                        this.nohandlerfound(processedrequest, response);
                        return;
                    }
                //根据处理器寻找对应的处理器适配器
                    handleradapter ha = this.gethandleradapter(mappedhandler.gethandler());
                    string method = request.getmethod();
                    boolean isget = "get".equals(method);
                    if (isget || "head".equals(method)) {
                        long lastmodified = ha.getlastmodified(request, mappedhandler.gethandler());
                        if (this.logger.isdebugenabled()) {
                            this.logger.debug("last-modified value for [" + getrequesturi(request) + "] is: " + lastmodified);
                        }

                        if ((new servletwebrequest(request, response)).checknotmodified(lastmodified) && isget) {
                            return;
                        }
                    }

                    if (!mappedhandler.applyprehandle(processedrequest, response)) {
                        return;
                    }
                   //返回视图
                    mv = ha.handle(processedrequest, response, mappedhandler.gethandler());
                    if (asyncmanager.isconcurrenthandlingstarted()) {
                        return;
                    }

                    this.applydefaultviewname(request, mv);
                    mappedhandler.applyposthandle(processedrequest, response, mv);
                } catch (exception var19) {
                    dispatchexception = var19;
                }

                this.processdispatchresult(processedrequest, response, mappedhandler, mv, dispatchexception);
            } catch (exception var20) {
                this.triggeraftercompletion(processedrequest, response, mappedhandler, var20);
            } catch (error var21) {
                this.triggeraftercompletionwitherror(processedrequest, response, mappedhandler, var21);
            }

        } finally {
            if (asyncmanager.isconcurrenthandlingstarted()) {
                if (mappedhandler != null) {
                    mappedhandler.applyafterconcurrenthandlingstarted(processedrequest, response);
                }
            } else if (multipartrequestparsed) {
                this.cleanupmultipart(processedrequest);
            }

        }
    }    

二、servletcontext、applicationcontext和webapplicationcontext区别?

  1、servletcontext:是一个web应用的上下文,是一个全局信息的存储空间,代表当前的web应用。servlet容器(如tomcat)在启动一个webapp时,会为它创建一个servletcontext对象,即servlet上下文环境。每个webapp都有唯一的servletcontext对象。同一个webapp的所有servlet对象共享一个serveltcontext,servlet对象可以通过servletcontext来访问容器中的各种资源。servletcontext在web应用启动时创建,在web应用服务关闭时释放。

   2、applicationcontext:它是建立在beanfactory基础之上的,除了包含beanfactory的所有功能之外,在国际化支持、资源访问、事件传播等方面进行了良好的支持。其体系结构如下图。

  applicationcontext接口有三个常用的实现类,如下:

类名称 功能描述
classpathxmlapplicationcontext
从类路径classpath中寻找指定的xml配置文件,找到并装载
完成applicationcontext的实例化工作
filesystemxmlapplicationcontext
从指定的文件系统路径中寻找指定的xml配置文件,找到并装载
完成applicationcontext的实例化工作
xmlwebapplicationcontext 从web应用中寻找指定的xml配置文件,找到并装载完成applicationcontext的实例化工作

  与beanfactory不同的是,applicationcontext容器实例化后会自动对所有的单实例bean进行实例化与依赖关系的装配,使之处于待用状态。而beanfactory容器实例化后并不会自动实例化bean,只有当bean被使用时beanfactory容器才会对该bean进行实例化与依赖关系的装配。

  3、webapplicationcontext:它是专门为web应用提供的,它允许从相对于web根目录路径中装载配置文件完成初始化;从webapplicationcontext中可以获得servletcontext的引用,同时为了方便web应用访问spring应用上下文,webapplicationcontext也将作为一个属性放到servletcontext中,可以通过webapplicationcontextutils的getwebapplicationcontext(servletcontext sc)方法获取。

三、总结

  applicationcontext是spring的核心,context为上下文环境,在web应用中,会用到webapplicationcontext,它继承自applicationcontext。在springmvc启动过程中,contextloaderlistener类起着至关重要的作用。它读取web.xml中配置的context-param中的配置文件,提前在web容器初始化前准备业务对应的application context,将创建好的webapplicationcontext放置于servletcontext属性中,这样我们只要得到servlet就可以得到webapplicationcontext对象,并利用这个对象访问spring容器管理的bean。

四、参考资料

  1、https://www.cnblogs.com/runforlove/p/5688731.html

  2、spring源码深度解析(郝佳)

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网