目录
一、概述
MVC大家比较熟悉
- M即model,是业务处理层,与我们开发中的(service、dao、model)等对应起来;
- V即view,是视图层,以前jsp、freemarker、velocity等,现在都是前后端分离了。使用@ResponseBody注解把Controller方法返回的对象通过转换器转换成指定的格式(如json/xml/protobuf)后,再写入到Response对象的body区,不再走视图解析器,把渲染到工作交给前端去做。
- C,即controller,控制器,可以分为前端控制器(负责请求的分发,即DispatcherServlet)、映射处理器(uri与处理方法的映射,即HandlerMapping)、业务控制器(即我们的controller层)、视图解析器(即ViewResolver)。
二、类图
如上图类的继承关系可知,DispatcherServlet就是一个Servlet.
三、初始化过程
了解Servlet的都知道在Servlet中主要的方法有:
- init 初始化方法
- service 用于处理请求的方法
- destroy servlet的销毁方法
3.1、HttpServletBean类初始化方法 init
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//将Servlet中配置的参数封装到pvs变量中,requiredProperties为必须参数,如果没
//配置将报异常
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) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
// 模板方法,子类初始化入口方法,在子类中可以做一些具体的实现方法
initServletBean();
}
3.2、在 FrameworkServlet 中实现了 initServletBean 方法
@Override
protected final void initServletBean() throws ServletException {
// 略 .....
try {
//主要方法,主要实现了 初始化 WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
// 略 .....
}
}
3.3、initWebApplicationContext 方法
protected WebApplicationContext initWebApplicationContext() {
// 略...
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.
synchronized (this.onRefreshMonitor) {
onRefresh(wac); //重点方法
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
initWebApplicationContext方法做了三件事:
- 获取spring的根容器rootContext。
- 设置webApplicationContext并根据情况调用onRefresh方法。
- 将webApplicationContext设置到ServletContext中。
3.4、DispatcherServlet中的 onRefresh 方法
@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);
//初始化FlashMapManager,只知道是重定向时用来保存数据用的,没有使用过,这里不进行分析
initFlashMapManager(context);
}
下面贴的代码的几个初始化代码的逻辑其实是一样的,分为三步,以initHandlerMappings进行说明
- 判断是否寻找所有容器中实现HandlerMapping接口的,是则寻找所有容器中实现了HandlerMapping接口的对象,设置到handlerMappings对象中并排序(平常开发中走的都是这个分支)
- 否的话,只找寻到前容器实现了HandlerMapping接口的对象
- 如果上面查找后为空的话,则加载配置文件中的类并实例化,再设置到handlerMappings中。注意的是:这个配置文件位置是:org/springframework/web/servlet/DispatcherServlet.properties
至此,DispatcherServlet的初始化工作已经完成。
四、请求过程
上文介绍过 Servlet中处理请求的为 service 方法。下面先看service方法的处理逻辑
4.1、HttpServlet中的 service方法
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;
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)) {
// 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);
}
}
从源码中可得知 HttpServlet中 service 方法根据请求方法,把请求具体交给了 doGet、doPost、doPut等方法来处理
4.2、FrameworkServlet 中的 service 方法
在FrameworkServlet中重写了 service 方法同时也重写了 doGet、doPost,doPut等方法,源码如下所示
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 主要通过此方法实现请求的分发
processRequest(request, response);
}
有源码可知,在doGet 方法中有调用了 processRequest方法,其他doXX中同样调用了此方法,接下来看看 此方法。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 获取LocaleContextHolder 中保存的LocalContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//获取当前请求的LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
//获取RequestContextHolder中保存的RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//获取当前请求的ServletRequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 略 .....
//将当前请求的LocaleContext和ServletRequestAttributes保存到
//LocaleConextHolder和RequestContextHolder中
initContextHolders(request, localeContext, requestAttributes);
try {
//模板方法,在子类中实现
doService(request, response);
}catch (ServletException | IOException ex) {
//略 ....
}catch (Throwable ex) {
//略 ....
}finally {
//恢复之前的LocaleContext和ServletRequestAttributes到
//LocaleConextHolder和RequestContextHolder中
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
//发布ServletRequestHandledEvent消息
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
说明: 由于在请求时把LocaleContext和ServletRequestAttributes保存到了LocaleContextHolder和RequestContextHolder中,这两个类都是在ThreadLocal中所有在后续的方法中可以根据通过这两个类获取LocalContext和ServletRequestAttributes,从而获取HttpServletRequest、HttpServletResponse和HttpSession。
4.3、DispatcherServlet中实现了 doService方法,
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//省略 部分代码 .....
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);
}
}
}
}
有源码可知 doService方法中主要是调用了 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.
//根据 request 查找对应的 Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//根据 Handler查找执行此Handler的 handler适配器
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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//执行拦截器的前置方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//实际调用 Handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//执行拦截器中的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//处理返回结果。包括处理异常、渲染页面、发出完成通知调用拦截器的 afterComletion方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", 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);
}
}
}
}
五、总结
本篇文章主要是分析了DispatcherServlet 的初始化和请求的响应过程,接下来将会分析 Handler的查找过程。