spring mvc DispatcherServlet详解之前传---FrameworkServlet
2016-03-29 17:50
543 查看
springmvcDispatcherServlet详解之前传---FrameworkServlet
做项目时碰到Controller不能使用aop进行拦截,从网上搜索得知:使用springmvc启动了两个context:applicationContext和WebapplicationContext。首先我们来了解applicationContext和WebapplicationContext区别和联系吧
1.ApplicationContext和WebApplicationContext是继承关系
/** *Interfacetoprovideconfigurationforawebapplication.Thisisread-onlywhile *theapplicationisrunning,butmaybereloadediftheimplementationsupportsthis. * *<p>Thisinterfaceaddsa{@codegetServletContext()}methodtothegeneric *ApplicationContextinterface,anddefinesawell-knownapplicationattributename *thattherootcontextmustbeboundtointhebootstrapprocess. * *<p>Likegenericapplicationcontexts,webapplicationcontextsarehierarchical. *Thereisasinglerootcontextperapplication,whileeachservletintheapplication *(includingadispatcherservletintheMVCframework)hasitsownchildcontext. * *<p>Inadditiontostandardapplicationcontextlifecyclecapabilities, *WebApplicationContextimplementationsneedtodetect{@linkServletContextAware} *beansandinvokethe{@codesetServletContext}methodaccordingly.*/ publicinterfaceWebApplicationContextextendsApplicationContext{
2.ContextLoaderListener创建基于web的应用根applicationContext并将它放入到ServletContext.
applicationContext加载或者卸载spring管理的beans。在structs和springmvc的控制层都是这样使用的。
3.DispatcherServlet创建自己的WebApplicationContext并管理这个WebApplicationContext里面的handlers/controllers/view-resolvers.
4.当ContextLoaderListener和DispatcherServlet一起使用时,ContextLoaderListener先创建一个根applicationContext,然后DispatcherSerlvet创建一个子applicationContext并且绑定到根applicationContext。
首先来看看web.xml的定义:
一般的web应用,通过ContextLoaderListener监听,ContextLoaderListener中加载的context成功后,spring将applicationContext存放在ServletContext中key值为"org.springframework.web.context.WebApplicationContext.ROOT"的attribute中。
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:conf/applicationContext*.xml</param-value> </context-param>
DispatcherServlet加载的context成功后,会将applicationContext存放在org.springframework.web.servlet.FrameworkServlet.CONTEXT.+(servletName)的attribute中。
<servlet> <servlet-name>mvcServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:conf/spring-dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
当然,如果没有指定*servlet.xml配置,则默认使用DispatcherServlet的默认配置DispatcherServlet.properties
#DefaultimplementationclassesforDispatcherServlet'sstrategyinterfaces. #UsedasfallbackwhennomatchingbeansarefoundintheDispatcherServletcontext. #Notmeanttobecustomizedbyapplicationdevelopers. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
简单的来说:springbean的管理在applicationContext中,ContextLoaderListener的作用:
1.将applicationContext的生命周期和servletContext的生命周期联系到一起。
2.自动管理applicationContext的创建,ContextLoaderListener给我们提供了一个便利,不用显式的去创建applicationContext。
DispatcherServlet相关bean的管理在
WebApplicationContext,ServletContextListener创建WebApplicationContext,WebApplicationContext可以访问
ServletContext/ServletContextAware这些bean,还可以访问getServletContext方法。
正式的官方文档:
AwebapplicationcandefineanynumberofDispatcherServlets.Eachservletwilloperateinitsownnamespace,loadingitsownapplicationcontextwithmappings,handlers,etc.OnlytherootapplicationcontextasloadedbyContextLoaderListener,ifany,willbeshared. AsofSpring3.1,DispatcherServletmaynowbeinjectedwithawebapplicationcontext,ratherthancreatingitsowninternally.ThisisusefulinServlet3.0+environments,whichsupportprogrammaticregistrationofservletinstances.
在一个web应用中使用多个DispatcherServlet,每个servlet通过自己的命名空间来获取自己的webapplicationContext,然后加载此applicationContext里面的hangdlerMapping,hangdlerAdapter等等。ContextLoaderListener加载根application,所有子applicationContext共享根applicationContext。
从版本3.1后,spring支持将DispatcherServlet注入到根applicationContext,而不用创建自己的webapplicationContext,这主要为支持servlet3.0以上版本环境的要求,因为servlet3.0以上版本支持使用编程的方式来注册servlet实例。
spring支持使用多层层次applicationContext,通常我们使用两层结构就够了。
接下来,通过深入源代码层来探究WebApplicationContext是如何创建的?
1.DispatcherServlet的初始化过程使用到applicationContext。
我们知道DispatcherServlet直接继承自FrameworkServlet,而FrameworkServlet又继承了HttpServletBean和ApplicationContextAware。
2.FrameworkServlet实现了ApplicationContextAware接口的setApplicationContext()方法,可知DispatcherServlet的applicationContext来自FrameworkServlet。
/** *CalledbySpringvia{@linkApplicationContextAware}toinjectthecurrent *applicationcontext.ThismethodallowsFrameworkServletstoberegisteredas *Springbeansinsideanexisting{@linkWebApplicationContext}ratherthan *{@link#findWebApplicationContext()finding}a *{@linkorg.springframework.web.context.ContextLoaderListenerbootstrapped}context. *<p>Primarilyaddedtosupportuseinembeddedservletcontainers. *@since4.0 */ @Override publicvoidsetApplicationContext(ApplicationContextapplicationContext){ if(this.webApplicationContext==null&&applicationContextinstanceofWebApplicationContext){ this.webApplicationContext=(WebApplicationContext)applicationContext; this.webApplicationContextInjected=true; } }
3.FrameworkServlet的setApplicationContext()方法中WebApplicationContext是如何实例化的呢?
FrameworkServlet继承自HttpServletBean,HttpServletBean的初始化方法:
/** *Mapconfigparametersontobeanpropertiesofthisservlet,and *invokesubclassinitialization. *@throwsServletExceptionifbeanpropertiesareinvalid(orrequired *propertiesaremissing),orifsubclassinitializationfails. */ @Override publicfinalvoidinit()throwsServletException{ if(logger.isDebugEnabled()){ logger.debug("Initializingservlet'"+getServletName()+"'"); } //Setbeanpropertiesfrominitparameters. try{ PropertyValuespvs=newServletConfigPropertyValues(getServletConfig(),this.requiredProperties); BeanWrapperbw=PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoaderresourceLoader=newServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class,newResourceEditor(resourceLoader,getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs,true); } catch(BeansExceptionex){ logger.error("Failedtosetbeanpropertiesonservlet'"+getServletName()+"'",ex); throwex; } //Letsubclassesdowhateverinitializationtheylike. initServletBean(); if(logger.isDebugEnabled()){ logger.debug("Servlet'"+getServletName()+"'configuredsuccessfully"); } }
FrameworkServlet实现了initServletBean();
/** *Overriddenmethodof{@linkHttpServletBean},invokedafteranybeanproperties *havebeenset.Createsthisservlet'sWebApplicationContext. */ @Override protectedfinalvoidinitServletBean()throwsServletException{ getServletContext().log("InitializingSpringFrameworkServlet'"+getServletName()+"'"); if(this.logger.isInfoEnabled()){ this.logger.info("FrameworkServlet'"+getServletName()+"':initializationstarted"); } longstartTime=System.currentTimeMillis(); try{ this.webApplicationContext=initWebApplicationContext(); initFrameworkServlet(); } catch(ServletExceptionex){ this.logger.error("Contextinitializationfailed",ex); throwex; } catch(RuntimeExceptionex){ this.logger.error("Contextinitializationfailed",ex); throwex; } if(this.logger.isInfoEnabled()){ longelapsedTime=System.currentTimeMillis()-startTime; this.logger.info("FrameworkServlet'"+getServletName()+"':initializationcompletedin"+ elapsedTime+"ms"); } }
最终追踪到FrameworkServlet的initWebApplicationContext()方法
/** *InitializeandpublishtheWebApplicationContextforthisservlet. *<p>Delegatesto{@link#createWebApplicationContext}foractualcreation *ofthecontext.Canbeoverriddeninsubclasses. *@returntheWebApplicationContextinstance *@see#FrameworkServlet(WebApplicationContext) *@see#setContextClass *@see#setContextConfigLocation */ protectedWebApplicationContextinitWebApplicationContext(){ WebApplicationContextrootContext= WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContextwac=null; if(this.webApplicationContext!=null){ //Acontextinstancewasinjectedatconstructiontime->useit wac=this.webApplicationContext; if(wacinstanceofConfigurableWebApplicationContext){ ConfigurableWebApplicationContextcwac=(ConfigurableWebApplicationContext)wac; if(!cwac.isActive()){ //Thecontexthasnotyetbeenrefreshed->provideservicessuchas //settingtheparentcontext,settingtheapplicationcontextid,etc if(cwac.getParent()==null){ //Thecontextinstancewasinjectedwithoutanexplicitparent->set //therootapplicationcontext(ifany;maybenull)astheparent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if(wac==null){ //Nocontextinstancewasinjectedatconstructiontime->seeifone //hasbeenregisteredintheservletcontext.Ifoneexists,itisassumed //thattheparentcontext(ifany)hasalreadybeensetandthatthe //userhasperformedanyinitializationsuchassettingthecontextid wac=findWebApplicationContext(); } if(wac==null){ //Nocontextinstanceisdefinedforthisservlet->createalocalone wac=createWebApplicationContext(rootContext); } if(!this.refreshEventReceived){ //EitherthecontextisnotaConfigurableApplicationContextwithrefresh //supportorthecontextinjectedatconstructiontimehadalreadybeen //refreshed->triggerinitialonRefreshmanuallyhere. onRefresh(wac); } if(this.publishContext){ //Publishthecontextasaservletcontextattribute. StringattrName=getServletContextAttributeName(); getServletContext().setAttribute(attrName,wac); if(this.logger.isDebugEnabled()){ this.logger.debug("PublishedWebApplicationContextofservlet'"+getServletName()+ "'asServletContextattributewithname["+attrName+"]"); } } returnwac; }
我们来分析整个流程吧:
1.获取根applicationContext。
WebApplicationContextrootContext= WebApplicationContextUtils.getWebApplicationContext(getServletContext()); /** *Findtheroot{@linkWebApplicationContext}forthiswebapp,typically *loadedvia{@linkorg.springframework.web.context.ContextLoaderListener}. *<p>Willrethrowanexceptionthathappenedonrootcontextstartup, *todifferentiatebetweenafailedcontextstartupandnocontextatall. *@paramscServletContexttofindthewebapplicationcontextfor *@returntherootWebApplicationContextforthiswebapp,or{@codenull}ifnone *@seeorg.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE */ publicstaticWebApplicationContextgetWebApplicationContext(ServletContextsc){ returngetWebApplicationContext(sc,WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); } /** *Findacustom{@linkWebApplicationContext}forthiswebapp. *@paramscServletContexttofindthewebapplicationcontextfor *@paramattrNamethenameoftheServletContextattributetolookfor *@returnthedesiredWebApplicationContextforthiswebapp,or{@codenull}ifnone */ publicstaticWebApplicationContextgetWebApplicationContext(ServletContextsc,StringattrName){ Assert.notNull(sc,"ServletContextmustnotbenull"); Objectattr=sc.getAttribute(attrName); if(attr==null){ returnnull; } if(attrinstanceofRuntimeException){ throw(RuntimeException)attr; } if(attrinstanceofError){ throw(Error)attr; } if(attrinstanceofException){ thrownewIllegalStateException((Exception)attr); } if(!(attrinstanceofWebApplicationContext)){ thrownewIllegalStateException("ContextattributeisnotoftypeWebApplicationContext:"+attr); } return(WebApplicationContext)attr; }
2.判断webapplicationContext是否存在?存在的话就重利用该webapplicationContext
protectedvoidconfigureAndRefreshWebApplicationContext(ConfigurableWebApplicationContextwac){ if(ObjectUtils.identityToString(wac).equals(wac.getId())){ //Theapplicationcontextidisstillsettoitsoriginaldefaultvalue //->assignamoreusefulidbasedonavailableinformation if(this.contextId!=null){ wac.setId(this.contextId); } else{ //Generatedefaultid... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX+ ObjectUtils.getDisplayString(getServletContext().getContextPath())+"/"+getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(newSourceFilteringListener(wac,newContextRefreshListener())); //Thewacenvironment's#initPropertySourceswillbecalledinanycasewhenthecontext //isrefreshed;doiteagerlyheretoensureservletpropertysourcesareinplacefor //useinanypost-processingorinitializationthatoccursbelowpriorto#refresh ConfigurableEnvironmentenv=wac.getEnvironment(); if(envinstanceofConfigurableWebEnvironment){ ((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(),getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); wac.refresh(); }
不存在的话,根据配置的属性名去查询:
/** *Retrievea{@codeWebApplicationContext}fromthe{@codeServletContext} *attributewiththe{@link#setContextAttributeconfiguredname}.The *{@codeWebApplicationContext}musthavealreadybeenloadedandstoredinthe *{@codeServletContext}beforethisservletgetsinitialized(orinvoked). *<p>Subclassesmayoverridethismethodtoprovideadifferent *{@codeWebApplicationContext}retrievalstrategy. *@returntheWebApplicationContextforthisservlet,or{@codenull}ifnotfound *@see#getContextAttribute() */ protectedWebApplicationContextfindWebApplicationContext(){ StringattrName=getContextAttribute(); if(attrName==null){ returnnull; } WebApplicationContextwac= WebApplicationContextUtils.getWebApplicationContext(getServletContext(),attrName); if(wac==null){ thrownewIllegalStateException("NoWebApplicationContextfound:initializernotregistered?"); } returnwac; }
3.如果还查询不到WebapplicationContext,那么就创建一个新的WebapplicationContext,并绑定到rootWebapplicationContext上:
/** *InstantiatetheWebApplicationContextforthisservlet,eitheradefault *{@linkorg.springframework.web.context.support.XmlWebApplicationContext} *ora{@link#setContextClasscustomcontextclass},ifset. *<p>Thisimplementationexpectscustomcontextstoimplementthe *{@linkorg.springframework.web.context.ConfigurableWebApplicationContext} *interface.Canbeoverriddeninsubclasses. *<p>Donotforgettoregisterthisservletinstanceasapplicationlisteneronthe *createdcontext(fortriggeringits{@link#onRefreshcallback},andtocall *{@linkorg.springframework.context.ConfigurableApplicationContext#refresh()} *beforereturningthecontextinstance. *@paramparenttheparentApplicationContexttouse,or{@codenull}ifnone *@returntheWebApplicationContextforthisservlet *@seeorg.springframework.web.context.support.XmlWebApplicationContext */ protectedWebApplicationContextcreateWebApplicationContext(ApplicationContextparent){ Class<?>contextClass=getContextClass(); if(this.logger.isDebugEnabled()){ this.logger.debug("Servletwithname'"+getServletName()+ "'willtrytocreatecustomWebApplicationContextcontextofclass'"+ contextClass.getName()+"'"+",usingparentcontext["+parent+"]"); } if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)){ thrownewApplicationContextException( "Fatalinitializationerrorinservletwithname'"+getServletName()+ "':customWebApplicationContextclass["+contextClass.getName()+ "]isnotoftypeConfigurableWebApplicationContext"); } ConfigurableWebApplicationContextwac= (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); wac.setConfigLocation(getContextConfigLocation()); configureAndRefreshWebApplicationContext(wac); returnwac; }
4.将子applicationContext发布到servletcontext上。
if(this.publishContext){ //Publishthecontextasaservletcontextattribute. StringattrName=getServletContextAttributeName(); getServletContext().setAttribute(attrName,wac); if(this.logger.isDebugEnabled()){ this.logger.debug("PublishedWebApplicationContextofservlet'"+getServletName()+ "'asServletContextattributewithname["+attrName+"]"); } }
/**
*ReturntheServletContextattributenameforthisservlet'sWebApplicationContext.
*<p>Thedefaultimplementationreturns
*{@codeSERVLET_CONTEXT_PREFIX+servletname}.
*@see#SERVLET_CONTEXT_PREFIX
*@see#getServletName
*/
publicStringgetServletContextAttributeName(){
returnSERVLET_CONTEXT_PREFIX+getServletName();
}
/**
*PrefixfortheServletContextattributefortheWebApplicationContext.
*Thecompletionistheservletname.
*/
publicstaticfinalStringSERVLET_CONTEXT_PREFIX=FrameworkServlet.class.getName()+".CONTEXT.";
最后,ContextLoaderListener启动时如何产生applicationContext呢?
见参考我的这篇文章:
小结:
我们就以FrameworkServlet的官方说明来结束吧。没有比它更合适的!希望你喜欢。
publicabstractclassFrameworkServlet
extendsHttpServletBean
implementsApplicationContextAwareBaseservletforSpring'swebframework.ProvidesintegrationwithaSpringapplicationcontext,inaJavaBean-basedoverallsolution.
Thisclassoffersthefollowingfunctionality:
ManagesaWebApplicationContextinstanceperservlet.Theservlet'sconfigurationisdeterminedbybeansintheservlet'snamespace.
Publisheseventsonrequestprocessing,whetherornotarequestissuccessfullyhandled.
SubclassesmustimplementdoService(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)tohandlerequests.BecausethisextendsHttpServletBeanratherthanHttpServletdirectly,beanpropertiesareautomaticallymappedontoit.SubclassescanoverrideinitFrameworkServlet()forcustominitialization.
Detectsa"contextClass"parameterattheservletinit-paramlevel,fallingbacktothedefaultcontextclass,XmlWebApplicationContext,ifnotfound.Notethat,withthedefaultFrameworkServlet,acustomcontextclassneedstoimplementtheConfigurableWebApplicationContextSPI.
Acceptsanoptional"contextInitializerClasses"servletinit-paramthatspecifiesoneormoreApplicationContextInitializerclasses.Themanagedwebapplicationcontextwillbedelegatedtotheseinitializers,allowingforadditionalprogrammaticconfiguration,e.g.addingpropertysourcesoractivatingprofilesagainstthecontext'senvironment.SeealsoContextLoaderwhichsupportsa"contextInitializerClasses"context-paramwithidenticalsemanticsforthe"root"webapplicationcontext.
Passesa"contextConfigLocation"servletinit-paramtothecontextinstance,parsingitintopotentiallymultiplefilepathswhichcanbeseparatedbyanynumberofcommasandspaces,like"test-servlet.xml,myServlet.xml".Ifnotexplicitlyspecified,thecontextimplementationissupposedtobuildadefaultlocationfromthenamespaceoftheservlet.
Note:Incaseofmultipleconfiglocations,laterbeandefinitionswilloverrideonesdefinedinearlierloadedfiles,atleastwhenusingSpring'sdefaultApplicationContextimplementation.ThiscanbeleveragedtodeliberatelyoverridecertainbeandefinitionsviaanextraXMLfile.
Thedefaultnamespaceis"'servlet-name'-servlet",e.g."test-servlet"foraservlet-name"test"(leadingtoa"/WEB-INF/test-servlet.xml"defaultlocationwithXmlWebApplicationContext).Thenamespacecanalsobesetexplicitlyviathe"namespace"servletinit-param.
AsofSpring3.1,FrameworkServletmaynowbeinjectedwithawebapplicationcontext,ratherthancreatingitsowninternally.ThisisusefulinServlet3.0+environments,whichsupportprogrammaticregistrationofservletinstances.SeeFrameworkServlet(WebApplicationContext)Javadocfordetails.
相关文章推荐
- java的序列化与反序列化及transient关键字
- javaer to go之基础
- MAC下Eclipse的启动
- eclipse 入门级使用
- Eclipse中给jar包导入JavaDoc的方法(javaDoc的用处)
- 数字游戏和JAVA的一些坑
- 使用基于注解的springmvc+mybatis的测试问题
- spring mvc4.1.x如何使用s:mvcUrl
- Java集合深入学习总结-ArrayList
- About Spring MVC
- Java 多数据源切换
- 利用Eclipse+maven编译Jedis源码成jar包和源码jar包
- 欢迎使用CSDN-markdown编辑器
- 关于Spring-JPA框架下使用多表查询的应用实例记录
- Java开发中的23种设计模式详解
- Prohibited package name: java.time
- eclipse安装freemarker插件【转】
- java.lang.IllegalArgumentException: Service Intent must be explicit
- OC与c混编实现Java的String的hashcode()函数
- java中的异常