您的位置:首页 > 编程语言 > Java开发

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呢?
见参考我的这篇文章:/article/4691956.html
小结:
我们就以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.


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: