1.springmvc中DispatcherServlet继承体系
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL10ERNl3ZE5UNNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLykDNzEDOyITM0ITMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2.概述
1)FrameworkServlet继承HttpServletBean,实现了HttpServletBean的initServletBean方法来初始化当前上下文,使用web.xml中的init-param作为属性,其中一个重要的配置contextConfigLocation,配置解析xml文件的路径,我们在xml经常看到配置的这个属性,这个是在FrameworkServlet进行处理,使用XmlWebApplicationContext来解析对应的xml配置文件
web.xml中的init-param主要有
- contextClass:默认上下文是XmlWebApplicationContext,可以自己指定对应的上下文
- contextInitializerClasses:指定一个或多个ApplicationContextInitializer,可以在容器刷新之前进行一些处理
- contextConfigLocation:xml配置文件,可以,或空格分割,
- namespace:servlet的命名空间
- contextAttribute:标明当前的ApplicationContext在ServletContext中的名称
还有一些其他的属性,具体看源码中FrameworkServlet的属性
2)其次就是重写了HttpServlet的doGet,doPost,doPut,doDelete,doOptions,doTrace等方法,每个方法都调用了processRequest方法,也就是每进来一个无论什么请求,都会调用processRequest方法
3.FrameworkServlet整个代码用工具翻译后的
https://blog.csdn.net/qq_39482039/article/details/118944033
4.实现ApplicationContextAware接口的作用
每个servlet只有一个上下文,由于默认使用XmlWebApplicationContext,这里实现ApplicationContextAware接口可以外部进行设置上下文,也就是ApplicationContext
5.FrameworkServlet实现了HttpServletBean的抽象方法initServletBean
/**
* {@link HttpServletBean}的重写方法,在任何bean属性之后调用
* *已经准备好了。创建此servlet的WebApplicationContext。
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
//设置当前的上下文
this.webApplicationContext = initWebApplicationContext();
//留给子类调用
initFrameworkServlet();
} catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
主要作用设置当前的上下文环境,最主要一个方法initWebApplicationContext
/**
* 初始化并发布此Servlet的WebApplicationContext。
* <p>代表{@link #createWebApplicationContext}进行实际创建
* 的上下文。可以在子类中覆盖。
*
* @return the WebApplicationContext instance
* @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
//找到ServletContext中父上下文,也就是ContextLoaderListener初始化的上下文
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//如果ApplicationContextAware注入的上下文存在
if (this.webApplicationContext != null) {
// 在构造时注入了一个上下文实例->使用它
wac = this.webApplicationContext;
//如果是可配置的上下文环境
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
//上下文尚未刷新->提供以下服务
//设置父上下文、设置应用程序上下文id等
if (cwac.getParent() == null) {
// 在没有显式父->集的情况下注入了上下文实例
// 根应用程序上下文(如果有;可以为空)作为父级
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
//如果上下文不存在,用contextAttribute查找是否在servletCoontext中注册
if (wac == null) {
//在构造时没有注入上下文实例->查看是否有
//已在servlet上下文中注册。如果存在,则假定
//父上下文(如果有)已经设置,并且
//用户已执行任何初始化,如设置上下文id
wac = findWebApplicationContext();
}
//没有找到创建一个可配置上下文环境,默认是xml文
if (wac == null) {
// 没有为此servlet定义上下文实例->创建本地实例
wac = createWebApplicationContext(rootContext);
}
//留给子类实现,自定义刷新
if (!this.refreshEventReceived) {
// 上下文不是具有刷新功能的ConfigurableApplicationContext
// 在构建时注入的支持或上下文已经
// 刷新->在此处手动触发初始刷新.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
//是否将上下文保存在ServletContext
if (this.publishContext) {
// 将上下文发布为servlet上下文属性。
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
- 获取父上下文也就是ContextLoaderListener初始化的上下文
- 如果ApplicationContextAware在外部注入了一个上下文使用它,如果是注解上下文环境,设置父上下文,并刷新上下文
- 在ServletContext中根据contextAttribute查找是否有指定的上下文
- 如果ServletContext中没有找到,使用默认XmlWebApplicationContext创建一个上下文
- refreshEventReceived是false默认会调用自定义刷新,留给子类实现
- 如果在web.xml配置为true,将当前上下保存在ServletContext
主要方法
1.configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
//应用程序上下文id仍设置为其原始默认值
//->根据可用信息分配更有用的id
if (this.contextId != null) {
wac.setId(this.contextId);
} else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 在上下文中,无论如何都将调用wac环境的#initPropertySources
//刷新;在此处急切地执行此操作,以确保servlet属性源到位
//用于以下#refresh之前的任何后处理或初始化
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
2.createWebApplicationContext创建一个上下文
/**
* 为这个servlet实例化WebApplicationContext,可以是默认的
* *{@link org.springframework.web.context.support.XmlWebApplicationContext}
* *或者{@link#setContextClass自定义上下文类}(如果已设置)。
* *委托给#createWebApplicationContext(ApplicationContext)。
*
* @param parent 要使用的父WebApplicationContext,如果没有则为{@code null}
* @return the WebApplicationContext for this servlet
* @see org.springframework.web.context.support.XmlWebApplicationContext
* @see #createWebApplicationContext(ApplicationContext)
*/
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
/**
* 为这个servlet实例化WebApplicationContext,可以是默认的
* *{@link org.springframework.web.context.support.XmlWebApplicationContext}
* *或者{@link#setContextClass自定义上下文类}(如果已设置)。
* *<p>此实现要求自定义上下文实现
* *{@link org.springframework.web.context.ConfigurableWebApplicationContext}
* *接口。可以在子类中重写。
* *<p>不要忘记在
* *已创建上下文(用于触发其{@link#onRefresh callback},并调用
* *{@link org.springframework.context.ConfigurableApplicationContext#refresh()}
* *在返回上下文实例之前。
*
* @param parent 要使用的父ApplicationContext,如果没有则为{@code null}
* @return 这个servlet的WebApplicationContext
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
//应用程序上下文id仍设置为其原始默认值
//->根据可用信息分配更有用的id
if (this.contextId != null) {
wac.setId(this.contextId);
} else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 在上下文中,无论如何都将调用wac环境的#initPropertySources
//刷新;在此处急切地执行此操作,以确保servlet属性源到位
//用于以下#refresh之前的任何后处理或初始化
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
//留给子类调用
postProcessWebApplicationContext(wac);
//调用ApplicationContextInitializer.initialize方法
applyInitializers(wac);
//加载或刷新配置,可能是xml或java配置类等
wac.refresh();
}
6.processRequest方法,设置当前请求的语言环境和属性,实际分派留给子类处理
/**
* 处理此请求,发布事件而不管结果如何。
* *<p>实际的事件处理由抽象
* *{@link#doService}模板方法。
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取当前的语言环境
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//给当前请求设置语言环境
LocaleContext localeContext = buildLocaleContext(request);
//获取当前请求范围或会话范围内的属性
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//绑定到当前请求
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//获取异步处理类
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
//处理请求实际分派,留给子类实现
doService(request, response);
} catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
} catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
} finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}