SpringMVC是一个基于Spring生态圈之上封装的一个处理web层请求的半封装框架。
那什么叫半封装框架?
半封装指的SpringMVC是基于servlet处理web层这一技术体系的扩展与延伸——
(1)基于servlet的体系,执行servlet规范,容器当中可以无缝整合
(2)基于spring生态圈的,那么它使用spring资源非常容易
SpringMVC遵循sun公司制定的Java web规范——servlet规范。servlet规范定义了web层的请求处理规范,主要涉及以下几个方法:
package javax.servlet;
import java.io.IOException;
/**
* Defines methods that all servlets must implement.
* /
public interface Servlet {
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
从继承图来看,也能明显看出SpringMVC实现了servlet规范。GenericServlet与HttpServlet是Sun公司定义的,从HTTPServletBean开始为SpringMVC框架层。
上下文就是Bean Container:bean的容器技术就算上下文,特别是bean的依赖注入 IOC。
启动项目前需要启动web容器——
监听外部配置文件有没有加载进来,加载后成为Spring的根上下文——>实际上就是提供基础底层的bean的组件
springMVC实现了servlet,其本质上就是一个servlet服务
web容器是在web.xml当中挂servlet服务的
SpringMVC
org.springframework.web.servlet.DispatcherServlet
1
dispatcher
*.do
那web容器是如何知道按照什么样的规范解读部署在其内部的servlet服务的?
答案还是servlet规范——负责解读部署的项目的web容器,其内部早就植入了servlet规范,以Tomcat为例:
如上图所示:在Apache-Tomcat中已经有了servlet规范
下图是无缝结合的场景——《SpringMVC在Tomcat当中是如何工作?》
Tomcat接受了http请求,内部环境就是一个servlet环境,因为其内部依赖了servlet规范。执行的第一步就是init模块,初始化的时候会读取web.xml中的文件信息。何以见得?我们看一下源码:
GenericServlet中的init方法读取了配置信息,会调用SpringMVC框架中子类HTTPServletBean中重写的初始化方法init(Config),上图中的read会读取servletConfig。
Tomcat收到http请求
4.1 第一步
调用Sun框架中的接口方法:javax.servlet.Servlet#service(ServletRequest req, ServletResponse res)方法
4.2 第二步
调用接口具体实现类方法——org.springframework.web.servlet.FrameworkServlet#service()方法
/**
* Override the parent class implementation in order to intercept PATCH
* requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String method = request.getMethod();
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
注释写的很清楚,重写该方法是为了拦截PATCH请求,这个PATCH请求是SpringMVC在Sun公司的基础上增加的一个请求类型
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
4.3 第三步
除了PATCH请求的其它方法都调用了父类(Sun框架)javax.servlet.http.HttpServlet中的service方法
判定请求方法类型,然后派发请求给各个处理函数
特别对GET方法进行了IMS(If-Modified-Since)比较
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// 服务器不支持IMS,直接开始处理逻辑
doGet(req, resp);
} else { //服务器支持IMS比较,那就先判断
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// 服务端资源有更新
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {//服务端资源无更新
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
}
如果你不了解IMS的概念,请阅读该博文HTTP的请求头标签 If-Modified-Since与Last-Modified
4.4 第四步
上面调用的各个处理函数org.springframework.web.servlet.FrameworkServlet都被重写,最终处理方法统一于其方法——processRequest
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 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, startTime, failureCause);
}
}
4.5 第五步
调用org.springframework.web.servlet.FrameworkServlet#processRequest()中的doService()方法,该抽象方法被DispatchServlet实现,因此最终调用org.springframework.web.servlet.DispatchServlet#doService()(源代码分析待续)
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + requestUri + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap();
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()) {
return;
}
// 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;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
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
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
业务流程图解析如下——
具体请参见另一篇博文《手写注解实现SpringMVC底层原理【JAVA核心】》