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); } } }
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
如对本文有疑问, 点击进行留言回复!!
荐 深入理解Java中的BigInteger和 BigDecimal,再也不怕面试了
tomact正常启动,但是在日志文件报错java.lang.NoClassDefFoundError: java/util/logging/Logger
servlet整合quartz:servlet中使用quartz,服务器启动时加载任务
荐 Java——集合中的Map接口通过HashMap类实现一些常用的方法
SpringBoot整合mybatis访问时报错Invalid bound statement (not found)
网友评论