SpringMVC启动加载、请求分析

简介

web.xml文件中的配置:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

下面看下DispatcherServlet的继承关系:

DispatcherServlet其实是一个Servlet,用于初始化各个功能的实现类,比如异常处理、视图处理、请求映射等;且继承了FrameworkServlet类,FrameworkServlet是Spring的基础Servlet,提供集成一个基于JavaBean的整体解决方案的Spring上下文;FrameworkServlet又继承了HttpServletBean,HttpServletBean主要做一些初始化的工作,如将web.xml中的配置参数设置到Servlet中. 再直观感受下DispatcherServlet上下文层次结构关系:

该图有助于下面分析启动、请求的分析理解,图片来自https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc

启动源码分析

由于DispatcherServlet是一个Servlet,启动时初始化首先调用init方法,进入其父类的 org.springframework.web.servlet.HttpServletBean#init方法中:

PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

获取了配置文件dispatcher-servlet.xml,跟进 initServletBean()方法,此处用了委派方式,具体实现交给子类FrameworkServlet去实现,主要看方法里这段代码:

this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();

跟进第一段代码,由于第一次启动,wac为nul,再进入初始化方法代码, org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.web.context.WebApplicationContext),再进入 org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.context.ApplicationContext)方法:

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 + "]");
}
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);

上面主要创建了ConfigurableWebApplicationContext对象,然后调用了configureAndRefreshWebApplicationContext方法用来做初始化工作,再进入到 org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext方法:

if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
    // The application context id is still set to its original default value
    // -> assign a more useful id based on available information
    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()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
    ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();

最后一句 wac.refresh()代码,跟进去发现调用的是 org.springframework.context.support.AbstractApplicationContext#refresh的方法,ConfigurableWebApplicationContext没有重写refresh方法,所以调用了父类的默认实现方法,进入这个方法,正是spring解析配置文件、加载bean的方法,这里不作分析,具体可参考之前的文章.

请求分析

服务起来后,在浏览器中输入 http://localhost:8082/ok,由于FrameworkServlet重写了Servlet的service方法,无疑会进入到该方法中:

protected void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

if (HttpMethod.PATCH.matches(request.getMethod())) {
    processRequest(request, response);
}
else {
    super.service(request, response);
}
}

该方法会判断请求的方式,无论哪种都会调用processRequest方法,进入该方法会看到这么一段代码:

doService(request, response);

直觉告诉我们,这个方法就是用来处理请求的,再跟进去,调用的是子类DispatcherServlet中的doService方法,该方法开始会设置请求头信息,下面有这么一段代码:

doDispatch(request, response);

同上,直接跟进去,摘取部分代码:

ModelAndView mv = null;
Exception dispatchException = null;
// Determine handler for the current request.
//获取MappedHandler
mappedHandler = getHandler(processedRequest);
// Determine handler adapter for the current request.
//获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mappedHandler.applyPostHandle(processedRequest, response, mv);
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);

进入HandlerAdapter的处理方法, ha.handle(processedRequest,response,mappedHandler.getHandler()),发现这么一段代码:

invocableMethod.invokeAndHandle(webRequest, mavContainer);

进入 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle:

//根据HandlerMapping中请求路径使用反射调用Controller中的求方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
    if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
        mavContainer.setRequestHandled(true);
        return;
    }
}
else if (StringUtils.hasText(this.responseReason)) {
    mavContainer.setRequestHandled(true);
    return;
}
mavContainer.setRequestHandled(false);
try {
    //处理返回结果
    this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
    if (logger.isTraceEnabled()) {
        logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
    }
    throw ex;
}

进入到 org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest方法:

// 
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
    StringBuilder sb = new StringBuilder("Invoking [");
    sb.append(getBeanType().getSimpleName()).append(".");
    sb.append(getMethod().getName()).append("] method with arguments ");
    sb.append(Arrays.asList(args));
    logger.trace(sb.toString());
}
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
    logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
}
return returnValue;

getMethodArgumentValues方法用于获取请求时所带的参数,doInvoke方法使用反射调用Controller中的方法,所以当断点执行到该方法中时,会调用OkController类中的ok方法,returnValue是ok方法的返回值,此时请求已经完成,下面再看响应.再回到 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle中, this.returnValueHandlers.handleReturnValue(returnValue,getReturnValueType(returnValue),mavContainer,webRequest)方法用于处理响应,进入该方法 org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue:

//根据returnValue、returnType获取不同的HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
//handler有很多子类,会调用不同的实现类
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);

由于OkController中的ok方法返回的是一个对象,所以进入到的handler的子类是 org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue:

ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
    throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, webRequest);

继续跟进writeWithMessageConverters方法,继续跟进 org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)方法,在该方法中会把响应写入到流中返回给客户端. 至此服务端的响应也简单的介绍完毕.

总结

实际的流程远比笔者介绍的复杂的太多,感兴趣的朋友可以打断点调试去探索,其中涉及到很多知识点都没有去过多的分析,后面的文章笔者会涉及;笔者非常想从tomcat容器启动,到Servlet的加载,再到Spring中bean初始化,然后tomcat容器处理请求,分配给Servlet去处理,再到DispatcherServlet前端控制器分发、处理,整个过程,限于笔者目前水平,没有把整个串联起来,形成一条完整的调用链,希望有朋友可以分享.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章