Spring mvc分析

前置知识

在阅读此文章前, 了解一下基础知识有助于阅读

  • Spring bean 的生命周期
  • ApplicationContext 的创建流程
  • Servlet 相关知识
  • Tomcat 的简单配置和使用

环境搭建

添加依赖

  • spring mvc

  • tomcat-embed-core

配置类

1
2
3
4
5
@EnableWebMvc
@ComponentScan
@Configuration
public class Config {
}

启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class TomcatServer {
private static final int PORT = 8080;
private static final String CONTEXT_PATH = "/web";

public static void main(String[] args) throws Exception {
Tomcat tomcatServer = new Tomcat();
tomcatServer.setPort(PORT);

AnnotationConfigWebApplicationContext context =
new AnnotationConfigWebApplicationContext();
// 注册配置类
context.register(Config.class);

DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
// 将DispatcherServlet添加到server中
Wrapper servlet = tomcatServer.
addServlet(CONTEXT_PATH, "DispatcherServlet", dispatcherServlet);
// 配置DispatcherServlet的路径, 一般是 /*
servlet.addMapping("/*");

// 启动
tomcatServer.start();
System.out.println("Tomcat started");
// wait
tomcatServer.getServer().await();

}
}

流程图

spring-mvc的整个流程大致如下图所示:

DispatcherServlet是整个流程的中心, 也是mvc的核心组件

图片来自网络

DispatcherServlet

DispatcherServlet继承的类和实现的接口

1
2
3
public class DispatcherServlet extends FrameworkServlet

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware

DispatcherServlet实现了Servlet接口, 了解过Servlet的同学都知道, Tomcat会通过servlet-mapping加载匹配当前urlServlet对象. 调用doService方法处理请求

setApplicationContext

如果使用的是springboot项目:

FrameworkServlet实现了ApplicationContextAware接口, 所以在bean初始化的时候会传入ApplicationContext,

使用springboot启动, 则该类的具体实现为AnnotationConfigServletWebServerApplicationContext

1
2
3
4
5
6
7
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
this.webApplicationContext = (WebApplicationContext) applicationContext;
this.webApplicationContextInjected = true;
}
}

static

spring-webmvc的resources目录下面有个DispatcherServlet.properties文配置件, 里面是strategy interfaces的默认实现类, DispatcherServlet在静态代码块里面加载这个配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
// PATH = DispatcherServlet.properties
ClassPathResource resource =
new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
// Properties
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
...
}
}

init

DispatcherServlet也是一个Servlet, Tomcat会调用每一个Servlet的初始方法先进行初始化, 然后再调用doService方法处理请求

父类HttpServletBean的初始方法

1
2
3
4
5
6
7
public final void init() throws ServletException {

...

// 这个方法在FrameworkServlet类中实现
initServletBean();
}

往下走到FrameworkServletinitServletBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
protected final void initServletBean() throws ServletException {

...

try {
// 主要初始化app context
this.webApplicationContext = initWebApplicationContext();
// 模板方法, 空实现
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}

...
}

继续往下走到initWebApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
protected WebApplicationContext initWebApplicationContext() {
// rootContext就是spring的congtext
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;
// 只有不是active才刷新, 但是在springboot启动过程中, context已经刷新了
// 就不用二次刷新了
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
// 配置并刷新context
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 集成在springboot这里就可以忽略了, 肯定不为null的
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
// 如果不是ConfigurableApplicationContext
// 或者在构造注入已经被refresh了
if (!this.refreshEventReceived) {
// 手动refresh, 注意这个不是刷新spring的context
// 而是初始化webAppContext的一些strategy objects
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}

if (this.publishContext) {
// 将wac放到servlet context,
// attrName =>
// org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcherServlet
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}

return wac;
}

来到 configureAndRefreshWebApplicationContext方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {

...

// servlet context区别spring web context
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// ContextRefreshListener会监听到context refresh完成之后publish的Event
wac.addApplicationListener(
new SourceFilteringListener(wac, new ContextRefreshListener()));

ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).
initPropertySources(getServletContext(), getServletConfig());
}

// 后置处理 context
postProcessWebApplicationContext(wac);
// 这个主要是init-param中指定的contextInitializerClasses
// 然后调用ApplicationContextInitializer的initialize方法
applyInitializers(wac);
// 可以参照spring-context refresh流程
wac.refresh();
}

到此context初始化方法就差不多了, 然后就是初始化spring mvc需要的一些组件

ContextRefreshListener

WebApplicationContext完成刷新之后, context会发布一个context刷新完成事件, 该Listener就可以获取到这事件调用onApplicationEvent进行处理

1
2
3
4
5
6
7
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}

接下来进入到FrameworkServletonApplicationEvent方法

1
2
3
4
5
6
7
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
// 防止多个线程同时初始化导致错误
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}

继续进入onRefresh方法, 在DispatcherServlet类中

1
2
3
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

进入initStrategies, 里有很多的初始化方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected void initStrategies(ApplicationContext context) {
// multipart文件解析器
initMultipartResolver(context);
// 本地资源解析器
initLocaleResolver(context);
// 主题解析器
initThemeResolver(context);
// 处理器映射解析器
// 该方法会向spring容器中注入RequestMappingHandlerMapping
// 并且调用自身的invokeInitMethods方法将controller中的每一个接口
// 包装为一个HandlerMethod, 并放在mappingRegistry中
initHandlerMappings(context);
// 适配器解析器
initHandlerAdapters(context);
// 异常处理器解析器
initHandlerExceptionResolvers(context);
// 请求转化为view
initRequestToViewNameTranslator(context);
// 视图解析器
initViewResolvers(context);
// 与重定向数据传输有关
initFlashMapManager(context);
}

到这里之后, spring mvc的初始化大致就完成了, 接下来就是处理请求了

doService

curl发送一个请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);

...

try {
// 内部调用 doDispatch 方法
doDispatch(request, response);
}
finally {
...
}
}

doDispatch

进入doDispatch方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

...

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
// 检查是不是multipart请求(文件上传)
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// 获取handler(HandlerChain)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// 获取handler的adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

...

// 在处理前执行的方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 默认view name
applyDefaultViewName(processedRequest, mv);
// 处理之后的方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}

...


processDispatchResult(processedRequest, response, mappedHandler, mv,
dispatchException);
}
catch (Exception ex) {
...
}

...
}

getHandler

接下来我们进入getHandler方法, 该方法会遍历mapping, 返回处理器链

1
2
3
4
5
6
7
8
9
10
11
12
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

我们再进入AbstractHandlerMapping看一下handler是如何获取到的

1
2
3
4
5
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
...
return executionChain;
}
getHandlerInternal

进入getHandlerInternal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// lookupPath就是资源路径, 不包含 contextPath
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}

lookupHandlerMethod方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 获取lookupPath对应的所有的match
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches,
request);
}

if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
// 如果有多个
if (matches.size() > 1) {
Comparator<Match> comparator =
new MatchComparator(getMappingComparator(request));
// 给match排序
// 详见 AntPathMatcher.compare(s1,s2)
matches.sort(comparator);
// 优先级最高的
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
// 如果是跨域的option请求
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
// 如果有两个一样的match, 相当于有两个处理请求的方法
// 此时spring-mvc就不知道用哪个, 直接抛出异常
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException ...
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// 处理match
handleMatch(bestMatch.mapping, lookupPath, request);
// 返回一个handlerMethod
// controller中加了RequestMapping的方法
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath,
request);
}
}

getHandlerAdapter

getHandlerAdapter获取handler的适配器, 通过适配器调用handler

1
2
3
4
5
6
7
8
9
10
11
12
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
// 如果能够支持handler
if (adapter.supports(handler)) {
return adapter;
}
}
}
// 没有适配器能适配handler就抛出异常
throw new ServletException...
}

adapter.handle

调用handler, 来到一个AbstractHandlerMethodAdapter抽象类中

1
2
3
4
5
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

return handleInternal(request, response, (HandlerMethod) handler);
}

handleInternal

handleInternal由子类RequestMappingHandlerAdapter实现,这也是springmvc默认的adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
checkRequest(request);

// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}

...

return mav;
}

invokeHandlerMethod

接下来进入invokeHandlerMethod方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// binderFactory 创建WebDataBinder用于请求参数的绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

ServletInvocableHandlerMethod invocableMethod =
createInvocableHandlerMethod(handlerMethod);

...
invocableMethod.invokeAndHandle(webRequest, mavContainer);
...

return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

invokeAndHandle

接着进入到invocableMethod.invokeAndHandle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

// invoke
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);

...
try {
// 处理返回值, 其中就涉及到object转为json, 或者解析string返回ModelAndView
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
...
}
}

invokeAndHandle

invokeAndHandle

1
2
3
4
5
6
7
8
9
10
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

// 从request中获取数据并封装为method的参数对象
// HandlerMethodArgumentResolverComposite解析参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}

doInvoke

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Nullable
protected Object doInvoke(Object... args) throws Exception {
// 反射设置方法可访问
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 通过反射调用controller中的
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
...
}
catch (InvocationTargetException ex) {
...
}
}

handleReturnValue

位于: RequestResponseBodyMethodProcessor

调用完成之后, 得到返回值, springmvc就处理返回值, 返回给客户端

1
2
3
4
5
6
7
8
9
10
11
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

// 选择handler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException...
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

来到handleReturnValue方法中

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 设置为已处理
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

// 响应
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

writeWithMessageConverters

这里主要是通过springmvc中的converts来处理方法的返回值, 并写到response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

Object body;
Class<?> valueType;
Type targetType;

// 例如 string
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType),
returnType.getContainingClass());
}


...

MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
...
selectedMediaType = contentType;
}
else {
...
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
...
}
return;
}

MediaType.sortBySpecificityAndQuality(mediaTypesToUse);

for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
...
}

if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
// 遍历所有的converter
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter =
(converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
// 如果支持 valueType -> targetType
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType,
selectedMediaType) :
converter.canWrite(valueType, selectedMediaType))
{
// convert前先获取advice进行增强
body = getAdvice().
beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
...
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
// 调用convert的write方法
genericConverter.write(body, targetType, selectedMediaType,
outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType,
outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
...
}
}
return;
}
}
}

if (body != null) {
...
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}

结尾

到此, springmvc的大致流程就结束了, 本篇文章就只是大致的说明了一下spring-mvc启动到处理请求的流程,spring-mvc中还有很多细节需要我们慢慢去看源码.

声明

  • 如有错误请联系作者更正