大家好,欢迎来到IT知识分享网。
DispatcherServlet其实是Servlet接口的实现类,它的本质其实就是一个Servlet。
在讲DispatcherServlet之前,首先介绍一下Servlet的生命周期。
Servlet的生命周期
1. init : 初始化
当Servlet第一次被请求时,服务器就会调用init()方法初始化一个Servlet对象,但是这个方法在后续的请求中不会再次被调用,仅仅只会调用一次。在调用这个方法时,Servlet容器会传入一个ServletConfig对象来对Servlet对象进行初始化。
当发送http请求时:StandardWrapper类会调用allocate()方法给Servlet分配一个初始化的实例。
private synchronized void initServlet(Servlet servlet)
throws ServletException {
// 如果servlet已被初始化,则return
if (instanceInitialized && !singleThreadModel) return;
// Call the initialization method of this servlet
try {
if( Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
Object[] args = new Object[] { facade };
SecurityUtil.doAsPrivilege("init",
servlet,
classType,
args);
success = true;
} finally {
if (!success) {
// destroy() will not be called, thus clear the reference now
SecurityUtil.remove(servlet);
}
}
} else {
// 调用init()方法初始化,facade: 是ServletConfig的实现类
// 这一步会转到DispatcherServlet初始化的过程
servlet.init(facade);
}
instanceInitialized = true;
} catch (UnavailableException f) {
unavailable(f);
throw f;
} catch (ServletException f) {
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw f;
} catch (Throwable f) {
ExceptionUtils.handleThrowable(f);
getServletContext().log(sm.getString("standardWrapper.initException", getName()), f);
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw new ServletException
(sm.getString("standardWrapper.initException", getName()), f);
}
}
IT知识分享网
2. service
每次客户端请求这个Servlet时,Servlet容器就会调用service()方法处理请求;在后续的请求中,Servlet容器就只会调用这个方法了。
3. destroy
当服务器关闭或者停止时,Servlet容器就会调用destroy()销毁这个Servlet。
DispatcherServlet初始化
由于DispatcherServlet本质就是一个Servlet,它对Servlet进行了封装,它的生命周期与Servlet一致,当调用init(ServletConfig servletConfig)方法初始化的时候,这个时候采用Java面向对象思想:当子类继承父类时,如果子类没有重写父类的方法,则首先调用父类的方法,它的父类给他做了很多初始化的工作。
由于DispatcherServlet没有重写init(ServletConfig servletConfig)方法,它会找到父类的init(ServletConfig servletConfig)方法,然后调用。
1. 在其父类GenericServlet类中重写了init(ServletConfig servletConfig)方法
2. 从第一步可以看出DispatcherServlet对象会调用init()初始化方法,由于DispatcherServlet类没有重写init()方法,它会调用父类的init()方法,观察继承结构可知,只有HttpServletBean类重写了init()方法
IT知识分享网 @Override
public final void init() throws ServletException {
// ServletConfigPropertyValues 是静态内部类,使用 ServletConfig 获取 web.xml 中配置的参数
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 使用 BeanWrapper 来构造 DispatcherServlet
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;
}
}
// 让子类实现的方法,这种在父类定义在子类实现的方式叫做模版方法模式
initServletBean();
}
3. 从上一步看到,它还要调用initServletBean方法,这个方法在DispatcherServlet类也没有重写,那只能往DispatcherServlet类的父类,HttpServletBean的子类找了,显而易见,只有FrameworkServlet类满足条件
initServletBean():建立 WebApplicationContext 容器(有时也称上下文),并加载 SpringMVC 配置文件中定义的 Bean 到该容器中,最后将该容器添加到 ServletContext 中。
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// context实例在构造函数中被注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
// 将 Spring 的容器设为 SpringMVC 容器的父容器
cwac.setParent(rootContext);
}
//刷新上下文环境
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 如果 WebApplicationContext 为空,则进行查找,能找到说明上下文已经在别处初始化。
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果servlet仍没有context实例,则以 Spring 的容器为父上下文建立一个新的。
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.
synchronized (this.onRefreshMonitor) {
// 模版方法,由其子类DispatcherServlet 实现
onRefresh(wac);
}
}
if (this.publishContext) {
// 发布这个 WebApplicationContext 容器到 ServletContext 中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
4. 终于,回到我们的子类DispatcherServlet中执行onRefresh(ApplicationContext context)方法中刷新上下文
IT知识分享网@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);
//初始化处理器映射器HandlerMapping --重点
initHandlerMappings(context);
//初始化处理器适配器
initHandlerAdapters(context);
//初始化处理器异常解析器
initHandlerExceptionResolvers(context);
//初始化请求到视图名翻译器
initRequestToViewNameTranslator(context);
//初始化视图解析器
initViewResolvers(context);
//初始化重定向数据管理器
initFlashMapManager(context);
}
下面对处理器映射器HandlerMapping类初始化进行一个说明:
initHandlerMappings() 方法从 SpringMVC 的容器及 Spring 的容器中查找所有的 HandlerMapping 实例,并把它们放入到 handlerMappings 这个 list集合 中。这个方法并不是对 HandlerMapping 实例的创建,HandlerMapping 实例是在上面 WebApplicationContext 容器初始化,即 SpringMVC 容器初始化的时候创建的。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
//从SpringMVC的IOC容器及Spring的IOC容器中查找HandlerMapping实例
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 按照一定排序规则放入list中
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// 如果没有,则加载默认的
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
到此,DispatcherServlet就初始化完成了。
然后,调用service方法开始处理http请求。
下面,主要讲DispatcherServelt如何处理请求,并封装结果响应。
DispatcherServelt处理请求
在分析处理请求之前,先看一下总体流程:
下面是更加详细的版本
1. HttpServlet类处理service
同样的分析,由于service(ServletResquest, ServletResponse)方法其父类HttpServlet已经重写好了,子类没有重写,则调用父类的service()方法。
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
// 调用子类FrameworkServlet判断请求是否是Patch请求或者null, 如果是的话交给子类处理
// 如果不是返回自己处理
service(request, response);
}
2. FrameworkServlet类处理service
@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 {
// 返回给HttpServlet类处理
super.service(request, response);
}
}
3 HttpServlet类继续处理service方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取请求方法名称
String method = req.getMethod();
// 判断请求方法类型,进入到对应的处理分支
// Get请求
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);
}
}
// Head请求
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
// Post
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
// Put
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
// Delete
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
// Options
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
// Trace
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
// 什么请求都不匹配,返回error
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);
}
}
4. 以Get请求为例,下面调用doGet请求,由于HttpServlet的子类FrameworkServlet类重写了HttpServlet的doGet方法,因此,将调用FrameworkServlet类的doGet请求。
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
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 {
// 终于,在这一步,子类DispatcherServlet重写了doService方法
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);
}
}
5. 千呼万唤,终于进入到DispatcherServlet类的doService()方法中
在doService方法中,主要是对请求做一些赋值,然后重中之重调用doDispatch方法处理请求。
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(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<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
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());
if (this.flashMapManager != null) {
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);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
try {
// 终于要处理request啦
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);
}
}
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
6. DispatcherServlet的doDispatch处理请求和返回响应
doDispatch处理的大致流程:
- 通过HandlerMapping获取请求对应的Handler
- 再通过Handler找到对应的HandlerAdapter
- HandlerAdapterd调用handler方法得到ModelAndView(连接“业务逻辑层”与“视图展示层”的桥梁)
- 通过ModelAndView 获得 View,再通过它的 Model 对 View 进行渲染
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 {
//1. 判断是否是文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 2. 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecutionChain 对象中。
// 返回的HandlerExecutionChain请求处理器链对象封装了handler和interceptors.
mappedHandler = getHandler(processedRequest);
// 如果没有对应的handler,返回404
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3. 获取处理请求的处理器适配器HandlerAdapter
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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 请求方法前置拦截,如果返回true 表示会执行到目标方法(请求方法) 如果返回false的情况下,直接return。
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 5. 这一步就是调用对应的controller控制器,并返回结果视图对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 如果是异步模式,则返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 6. 如果mv不为null,则给其ViewName设置初始默认值
applyDefaultViewName(processedRequest, mv);
// 7. 后置拦截器的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);
}
// 8. 处理返回结果;(异常处理、页面渲染、拦截器的afterCompletion触发等)
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()) {
//判断是否执行异步请求
if (mappedHandler != null) {
// 如果是的话,就替代拦截器的postHandle 和 afterCompletion方法执行
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 删除上传请求的资源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/7033.html