[SpringMVC]初始化源码分析
2018-04-09 本文已影响72人
程序员驿站
SpringMVCFlowDiagram.jpg
大家都知道Springmvc的核心是DispatcherServlet,而实现Servlet,类图关系;
屏幕快照 2018-04-09 上午10.26.15.png大家都知道Servlet的生命周期分为,init service destory;init和destroy只执行一次
那么DispatcherServlet会在这三个阶段都做了哪些事?
init 阶段
大概流程
HttpServletBean#init() --将配置参数映射到此servlet的bean属性上,调用子类初始化
-> 从初始化参数设置bean属性
-> 将当前servlet类转换一个BeanWrapper
-> 自动以属性编辑器
-> 检测配置的参数
-> initBeanWrapper(bw) 空是实现
-> FrameworkServlet#initServletBean
-> initWebApplicationContext(); --初始化WebApplicationContext
-> 先从servletContext上下文获取
-> 如果拿到。。。。
-> 没有获取到
-> 从配置名称为ServletContext属性中检索webapplication上下文。
-> createWebApplicationContext(); 创建webApplicationContext
-> 读取XmlWebApplicationContext.class
-> 初始化XmlWebApplicationContext
-> 设置环境 如果没有则创建StandardServletEnvironment
-> 设置父容器 WebApplicationContext
-> 设置configLocation
-> 配置并且重启WebApplicationContext
-> 设置servlet上下文
-> 设置配置信息
-> 设置namespace
-> 设置一个ContextRefreshListener
-> 给子类留下一个hook postProcessWebApplicationContext
-> applyInitializers 在webapplicationcontextinizerclass servlet init - param指定的任何应用程序上下文初始值设定项实例刷新之前,委托webapplicationcontextizerclass。
-> ConfigurableWebApplicationContext 刷新
-> initFrameworkServlet();
源码解析
1、先看下HttpServletBean#init()
/**
* 将配置参数映射到servlet的bean属性上,并且调用子类初始化 模板方法
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
*
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*/
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters
// 解析配置的init-param并且封装到PropertyValues.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
// 将当前的servlet类转换为BeanWrapper。从而能够被Spring对init-param的值进行注入
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
// 注册自定义属性编辑器
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 空实现,留给子类的钩子
initBeanWrapper(bw);
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.
// 留给子类实现,这里是被FrameworkServlet实现的
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
2、接着看下 FrameworkServlet# initServletBean();
/**
* 重写HttpServletBean的方法,在设置任何bean属性后调用。创建此servlet的WebApplicationContext
* 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 {
// 初始化WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
// 留给子类覆盖
initFrameworkServlet();
} 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");
}
}
3、初始化WebApplicationContext
/**
* 初始化并且发布servlet的WebApplicationContext的实例
* Initialize and publish the WebApplicationContext for this servlet.
* <p>Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
*
* @return the WebApplicationContext instance 返回WebApplicationContext实例
* @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
// 对servlet功能所使用的变量进行初始化
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
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
// 根据contextAttribute属性加载WebApplicationContext
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
//没有为此servlet定义上下文实例
wac = createWebApplicationContext(rootContext);
}
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);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
4、主要看下createWebApplicationContext(rootContext);
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
// XmlWebApplicationContext.class 默认
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
// 必须实现ConfigurableWebApplicationContext接口
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);
wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;
}
4、onRefresh(wac);
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* 初始化此servlet使用的策略对象。
* 以便初始化进一步的策略对象
* 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) {
// 初始化MultipartResolver 用来处理上传文件 默认是没有处理的,需要在上下文添加MultipartResolver处理器
// org.springframework.web.multipart.commons.CommonsMultipartResolver
// org.springframework.web.multipart.support.ByteArrayMultipartFileEditor
// org.springframework.web.multipart.support.StringMultipartFileEditor
initMultipartResolver(context);
// 初始化LocaleResolver 配置国际化的
initLocaleResolver(context);
// 初始化ThemeResolver 通过主题控制页面风格
initThemeResolver(context);
// 初始化HandlerMappings 由定义请求和处理程序对象之间映射的对象实现
initHandlerMappings(context);
// 初始化HandlerAdapters
initHandlerAdapters(context);
// 初始化HandlerExceptionResolvers 异常处理
initHandlerExceptionResolvers(context);
// 初始化RequestToViewNameTranslator
initRequestToViewNameTranslator(context);
// 初始化ViewResolvers
initViewResolvers(context);
// 初始化FlashMapManager
initFlashMapManager(context);
}
初始化阶段的工作基本做完了,各个方法细节东西,大家可以自己看下内部实现。每个方法的代码非常清晰。
逻辑处理阶段
调用栈
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:743)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:672)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:82)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:919)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:851)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
流程
我们发起http的get请求或者post请求,都是HttpServlet的service方法根据http协议的方法来区分的处理逻辑,见下面代码
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
Spring的FrameworkServlet重写了doGet或者doPost方法
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
整理处理的实际上processRequest方法,我们看下processRequest这个方法都干了啥?看了下这个方法的注释
/**
* 处理此请求,发布事件而不考虑结果。
实际事件处理由抽象doService模板方法执行
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 初始化当前时间,用户记录web请求的时间
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
// 上request的参数 放到ThreadLocal 里面供非controller层调用
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 ex) {
failureCause = ex;
throw ex;
} catch (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();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
} else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
} else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
doService被DispatcherServlet重写了,重点来了
/**
* 设置DispatcherServlet请求属性和委托给doDispatch以进行实际调度
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
注释是说明实际调度是doDispatch,哦这才是重点,迫不及待的看看他都实现了啥
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 将请求转换为多部分请求,并使多部分解析器可用
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 根据request查找对应的handler
mappedHandler = getHandler(processedRequest);
// 如果没有handler处理直接返回错误信息
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 确定当前请求的处理程序适配器.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理last-modified (如果处理程序支持)
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 已注册拦截器的预处理方法 如果返回为true 继续执行,返回false 该请求直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际调用处理程序.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(request, mv);
// 注册拦截器的事后处理方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
// 处理程序调用的结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
里面细节的方法 后续单独分析
destroy
这个阶段干的事情比较少,就是关闭一些东西
/**
* Close the WebApplicationContext of this servlet.
*
* @see org.springframework.context.ConfigurableApplicationContext#close()
*/
@Override
public void destroy() {
getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
// Only call close() on WebApplicationContext if locally managed...
if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
((ConfigurableApplicationContext) this.webApplicationContext).close();
}
}
总结
1、