springmvc核心转发器(中央控制器)DispatcherServlet分析

springmvc采用最常见的前端控制器模式(Front Controller Pattern)

是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。
springmvc中所有请求都会到达DiapatcherServlet,具体由 doDispatch(HttpServletRequest request, HttpServletResponse response)方法进行分发,

分发到具体的controller层。首先我们来看看DispatcherServlet继承关系.

                                                                                                                        dispacherservlet的继承关系

          HttpServlet继承了HttpServlet,并重写了init  方法:

 @Override

public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}

// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

<servlet>

<servlet-name>dispatcherServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 指定SpringMVC框架的配置文件的路径和名称 -->
<param-value>classpath*:conf/mvc/mvc-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

//构造ServletConfigPropertyValues过程中会查找web.xml配置,读取nit-param中的配置,并设置到ServletConfigPropertyValues中
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);//BeanWrapper 是spring内部实例化bean的方法,使用BeanWrapper实例化DispatcherServlet
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));//注册响应的属性编辑器(资源加载器),当类型是Resource的用ResourceEditor进行类型转换

initBeanWrapper(bw);//初始化bean

bw.setPropertyValues(pvs, true);//进行属性赋值

}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}

// Let subclasses do whatever initialization they like. //让子类做一写具体的处理
initServletBean();

if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}

比如上面这段配置,传递了contextConfigLocation参数,之后构造BeanWrapper,这里使用BeanWrapper,有2个理由:1. contextConfigLocation属性在FrameworkServlet中定义,HttpServletBean中未定义 2. 利用Spring的注入特性,只需要调用setPropertyValues方法就可将contextConfigLocation属性设置到对应实例中,也就是以依赖注入的方式初始化属性。然后设置DispatcherServlet中的contextConfigLocation属性(FrameworkServlet中定义)为web.xml中读取的contextConfigLocation参数,该参数用于构造SpringMVC容器上下文。

下面看下FrameworkServlet这个类,FrameworkServlet继承自HttpServletBean。

首先来看下该类覆写的initServletBean方法:

/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();

try {
this.webApplicationContext = initWebApplicationContext();//将servlet属性与spring上下文绑定,webApplicationContext作为上下文环境。
initFrameworkServlet();//不做任何处理,DispatcherServlet也不做任何处理
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}

if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}

来看看initWebApplicationContext具体代码实现:                           

 

protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());//获取根上下文,一般放在ServletContext上下文中,

键 WebApplicationContext.class.getName() + ".ROOT",值webApplicationContext
WebApplicationContext wac = null;

if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();//contextAttribute以这个属性名去servletContext中查找springmvc上下文。

//org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcherServlet attrname作为键名保存在servletContext上下文中
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);//创建WebApplicationContext上下文,并将相关配置加入其中
}

if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac); //模板方法在DispatcherServlet中具体实现,加载一些初始化设置。
}

if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();

getServletContext().setAttribute(attrName, wac);//将先创建的上下文放到ServletContext中去。
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}

return wac;
}

这里的根上下文是web.xml中配置的ContextLoaderListener监听器中根据contextConfigLocation路径生成的上下文

<!--加载spring上下文对象 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/cpic/root-context.xml</param-value>
</context-param>
<!--default enviroment for setting -->
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>development</param-value>
</context-param>

比如这段配置文件中根据classpath :conf/applicationContext.xml下的xml文件生成的根上下文。

最后看下DispatcherServlet。

DispatcherServlet覆写了FrameworkServlet中的onRefresh方法:

/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);//初始化各种策略的接口
}

/**
* Initialize the strategy objects that this servlet uses.
* <p>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);
}

//很明显,initStrategies方法内部会初始化各个策略接口的实现类。

总结一下各个Servlet的作用:

1. HttpServletBean

  主要做一些初始化的工作,将web.xml中配置的参数设置到Servlet中。比如servlet标签的子标签init-param标签中配置的参数。

2. FrameworkServlet

  将Servlet与Spring容器上下文关联。其实也就是初始化FrameworkServlet的属性webApplicationContext,这个属性代表SpringMVC上下文,它有个父类上下文,既web.xml中配置的ContextLoaderListener监听器初始化的容器上下文。

3. DispatcherServlet 

  初始化各个功能的实现类。比如异常处理、视图处理、请求映射处理等。

 

转载于:https://www.cnblogs.com/caibixiang123/p/8580692.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值