Spring5源码解析-论Spring DispatcherServlet的生命周期

Spring Web框架架构的主要部分是DispatcherServlet。也就是本文中重点介绍的对象。

在本文的第一部分中,我们将看到基于Spring的DispatcherServlet的主要概念:前端控制器模式。第二部分将专门介绍Spring应用程序中的执行链。接下来是DispatcherServlet类的解释。在最后一部分,我们将尝试开发一个自定义的dispatcher servlet

请注意,本文分析的DispatcherServlet来自Spring的5.0.0.RC3版本。如果使用不同的版本,则可能需要进行几个调整,其实因为分析的都是比较固定的东西,很少有改的。

什么是前端控制器模式?

在进入DispatcherServlet之前,我们需要了解一些关于它的概念基础。DispatcherServlet所隐含的关键概念其实就是前端控制器模式。

此模式为Web应用程序提供了一个中心入口点。该集中入口点将系统组件的共同特征进行重新组合。我们可以在那里找到安全资源,语言切换,会话管理,缓存或输入过滤的处理程序。这样做的一个很大的好处是:这个共同的入口点有助于避免代码重复。

因此,从技术上讲,前端控制器模式由一个捕获所有传入请求的类组成。之后,分析每个请求以知道哪个控制器以及哪个方法应该来处理该请求。

前端控制器模式有助于对以下询问做出最佳响应:

如何集中授权和认证

如何处理正确的视图渲染?

如何使用URL重写映射将请求发送到适当的控制器?

这个前台控制器模式包含5名参与者:

客户端:发送请求。

控制器:应用程序的中心点,捕获所有请求。

调度员:管理视图的选择,以呈现给客户端。

视图:表示呈现给客户端的内容。

帮助:帮助查看和/或控制器完成请求处理。

什么是DispatcherServlet的执行链?

标题可以看到,前端控制器模式有自己的执行链。这意味着它有自己的逻辑来处理请求并将视图返回给客户端:

请求由客户端发送。它到达作为Spring的默认前端控制器的DispatcherServlet类。

DispatcherServlet使用请求处理程序映射来发现将分析请求的控制器(controller)。接口org.springframework.web.servlet.HandlerMapping的实现返回一个包含org.springframework.web.servlet.HandlerExecutionChain类的实例。此实例包含可在控制器调用之前或之后调用的处理程序拦截器数组。你可以在Spring中有关于拦截器的文章中了解更多的信息。如果在所有定义的处理程序映射中找不到HandlerExecutionChain,这意味着Spring无法将URL与对应的控制器进行匹配。这样的话会抛出一个错误。

现在系统进行拦截器预处理并调用由映射处理器找到的相应的controller(其实就是在找到的controller之前进行一波拦截处理)。在controller处理请求后,DispatcherServlet开始拦截器的后置处理。在此步骤结束时,它从controller接收ModelAndView实例(整个过程其实就是 request请求->进入interceptors->controller->从interceptors出来->ModelAndView接收)。

DispatcherServlet现在将使用的该视图的名称发送到视图解析器。这个解析器将决定前台的展现内容。接着,它将此视图返回给DispatcherServlet,其实也就是一个“视图生成后可调用”的拦截器。

最后一个操作是视图的渲染并作为对客户端request请求的响应。

什么是DispatcherServlet?

通过上面讲到的前端控制器模式,我们可以很轻易的知道DispatcherServlet是基于Spring的Web应用程序的中心点。它需要传入请求,并在处理程序映射,拦截器,控制器和视图解析器的帮助下,生成对客户端的响应。所以,我们可以分析这个类的细节,并总结出一些核心要点。

下面是处理一个请求时DispatcherServlet执行的步骤:

  1. 策略初始化

DispatcherServlet是一个位于org.springframework.web.servlet包中的类,并扩展了同一个包中的抽象类FrameworkServlet。它包含一些解析器的私有静态字段(用于本地化,视图,异常或上传文件),映射处理器:handlerMapping和处理适配器:handlerAdapter(进入这个类的第一眼就能看到的)。DispatcherServlet非常重要的一个核心点就是是初始化策略的方法(protected void initStrategies(ApplicationContext context))。在调用onRefresh方法时调用此方法。最后一次调用是在FrameworkServlet中通过initServletBean和initWebApplicationContext方法进行的(initServletBean方法中调用initWebApplicationContext,后者调用onRefresh(wac))。initServletBean通过所提供的这些策略生成我们所需要的应用程序上下文。其中每个策略都会产生一类在DispatcherServlet中用来处理传入请求的对象。

基于篇幅,有些代码就不给贴示了,请在相应版本的源码中自行对照查找,此处只给一部分源码:

/**

* This implementation calls {@link #initStrategies}.

*/

@Override

protected void onRefresh(ApplicationContext context) {

initStrategies(context);

}

/**

* Initialize the strategy objects that this servlet uses.

*

May be overridden in subclasses in order to initialize further strategy objects.

*/

protected void initStrategies(ApplicationContext context) {

initMultipartResolver(context);

initLocaleResolver(context);

initThemeResolver(context);

initHandlerMappings(context);

initHandlerAdapters(context);

initHandlerExceptionResolvers(context);

initRequestToViewNameTranslator(context);

initViewResolvers(context);

initFlashMapManager(context);

}

需要注意的是,如果找的结果不存在,则捕获异常NoSuchBeanDefinitionException(下面两段代码的第一段),并采用默认策略。如果在DispatcherServlet.properties文件中初始定义的默认策略不存在,则抛出BeanInitializationException异常(下面两段代码的第二段)。默认策略如下:

/**

* Initialize the LocaleResolver used by this class.

*

If no bean is defined with the given name in the BeanFactory for this namespace,

* we default to AcceptHeaderLocaleResolver.

*/

private void initLocaleResolver(ApplicationContext context) {

try {

this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);

if (logger.isDebugEnabled()) {

logger.debug(“Using LocaleResolver [” + this.localeResolver + “]”);

}

}

catch (NoSuchBeanDefinitionException ex) {

// We need to use the default.

this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);

if (logger.isDebugEnabled()) {

logger.debug(“Unable to locate LocaleResolver with name ‘” + LOCALE_RESOLVER_BEAN_NAME +”’: using default [” + this.localeResolver + “]”);

}

}

}

抛出异常后调用getDefaultStrategy(因为容器里都是单例的存在,所以只需要判断基于这个接口的默认实现实例size为1即可,两个以上还能叫默认么,都有选择了):

/**

* Return the default strategy object for the given strategy interface.

* The default implementation delegates to {@link #getDefaultStrategies},

* expecting a single object in the list.

* @param context the current WebApplicationContext

* @param strategyInterface the strategy interface

* @return the corresponding strategy object

* @see #getDefaultStrategies

*/

protected T getDefaultStrategy(ApplicationContext context, Class strategyInterface) {

List strategies = getDefaultStrategies(context, strategyInterface);

if (strategies.size() != 1) {

throw new BeanInitializationException(

“DispatcherServlet needs exactly 1 strategy for interface [” + strategyInterface.getName() + “]”);

}

return strategies.get(0);

}

/**

* Create a List of default strategy objects for the given strategy interface.

* The default implementation uses the “DispatcherServlet.properties” file (in the same

* package as the DispatcherServlet class) to determine the class names. It instantiates

* the strategy objects through the context’s BeanFactory.

* @param context the current WebApplicationContext

* @param strategyInterface the strategy interface

* @return the List of corresponding strategy objects

*/

@SuppressWarnings(“unchecked”)

protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) {

String key = strategyInterface.getName();

String value = defaultStrategies.getProperty(key);

if (value != null) {

String[] classNames = StringUtils.commaDelimitedListToStringArray(value);

List strategies = new ArrayList<>(classNames.length);

for (String className : classNames) {

try {

Class

原文 

http://geek.csdn.net/news/detail/239731

PS:如果您想和业内技术大牛交流的话,请加qq群(527933790)或者关注微信公众 号(AskHarries),谢谢!

转载请注明原文出处:Harries Blog™ » Spring5源码解析-论Spring DispatcherServlet的生命周期

赞 (0)

分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址