springMvc 架构流程和各个组件

用户发送请求至前端控制器DispatcherServlet。

DispatcherServlet收到请求调用HandlerMapping处理器映射器。

处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

DispatcherServlet调用HandlerAdapter处理器适配器。

HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

Controller执行完成返回ModelAndView。

HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

ViewReslover解析后返回具体View。

DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

DispatcherServlet响应用户。

包含各个组件

前端控制器:DispatcherServlet 用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。

处理器映射器:HandlerMapping HandlerMapping负责根据用户请求找到Handler处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

处理器适配器:HandlerAdapter 通过 HandlerAdapter对处理器进行执行适配,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。因为SpringMVC中的Handler可以是任意的形式,只要能处理请求就ok,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。

处理器:Handler 它就是我们开发中要编写的具体业务处理接口。由 DispatcherServlet把用户请求转发到 Handler。由Handler对具体的用户请求进行处理。Handler的概念,也就是处理器。它直接应对着MVC中的C也就是Controller层,它的具体表现形式有很多,可以是类,也可以是方法。在Controller层中@RequestMapping标注的所有方法都可以看成是一个Handler,只要可以实际处理请求就可以是Handler。

视图解析器:View Resolver View Resolver负责将处理结果生成View 视图,View Resolver首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View视图对象(包括响应信息等),最后对View进行渲染将处理结果通过页面展示给用户。

视图:View SpringMVC 框架提供了很多的 View 视图类型的支持,包括:JstlView、FreeMarkerView等。最常用的视图就是 JSP。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面

源码分析

1.初始化流程

1
2
3
4
5
6
<context-param>   
<param-name>contextConfigLocation</param-name>  
<param-value>classpath:applicationContext03.xml</param-value>
</context-param>
<listener>  
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
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
//ContextLoaderListener 类调用contextInitialized
public void contextInitialized(ServletContextEvent event) {
//调用父类的
initWebApplicationContext(event.getServletContext());
}

/***********************************ContextLoader.java*******************************/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
try {
//创建web上下文,以保证它在ServletContext关闭时可用
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//配置了<context-param>和监听时,先加载IOC配置
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//将上下文设置到servlet容器中,当加载spring-mvc文件的时候直接使用即可
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
}

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
//..........................................略
//其实这个地方就是加载Spring容器配置
wac.refresh();
}

dispatcherServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

DispatcherServlet是MVC的心脏,继承了FrameworkServlet,FrameworkServlet又继承了HttpServletBean,HttpServletBean又继承了HttpServlet并且实现了Servlet的init方法。从顶层往子类往下看,一系列的模板方法!(父类调用,子类实现)

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
/****HttpServletBean.java********/
/**
* 重写了父类HttpServlet的init方法,进行配置文件的读取和MVC的初始化
*/
public final void init() throws ServletException {
//获取配置文件信息,准备设置Bean的属性对象PropertyValue
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
//有点长,直接跟进去到BeanWrapper包装接口实现类BeanWrapperImpl中看setValue
//将配置文件设置到上下文中,以备后面解析
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isDebugEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 进入正题:初始化ServletBean
initServletBean();
}

/**********FrameworkServlet.java**/
protected final void initServletBean() throws ServletException {
//..........................................略
try {
//重点在这里,初始化web 容器上下文信息
this.webApplicationContext = initWebApplicationContext();
//空壳方法,可用于拓展
initFrameworkServlet();
}
//..........................................略
}
/********FrameworkServlet.java*****/
protected WebApplicationContext initWebApplicationContext() {
//获取Spring容器初始化时候的上下文信息
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;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
//这个地方避免自定义上下文监听没有进行IOC容器初始化,再一次初始化
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//判断是否已经初始化过了web容器上下文
wac = findWebApplicationContext();
}
if (wac == null) {
// 开始初始化web容器上下文,过程跟IOC一样,调用了AbstractApplicationContext refresh方法
//然后在后置处理器中进行url和controller的绑定
//传入rootContext,在此基础上继续加载MVC资源
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
//开始初始化MVC九大组件
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}

return wac;
}

加载SpringMVC容器

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
 /********FrameworkServlet.java******/ 
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass();
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());
//将IOC容器作为Web容器的父容器
wac.setParent(parent);
//获取到web配置信息
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
//调用IOC容器初始化流程,实例化并依赖注入,初始化及回调后置处理器
configureAndRefreshWebApplicationContext(wac);
return wac;
}
/*******FrameworkServlet.java*******/
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
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());
}
}
//把之前ContextLoaderListener加载的web容器信息设置到WebApplicationContext
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
//请记住这个地方,Spring在上下文中添加了一个监听事件SourceFilteringListener,当IOC初始化完成后通过广播事件触发该监听,进行MVC九大组件的初始化
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);
//接下来就是IOC的流程了
wac.refresh();
}

2.HandlerMapping , HandlerAdapter初始化

HandlerMapping会作为一个Bean实例化到Spring 容器,它有以下几种模式:

  • BeanNameUrlHandlerMapping ,根据bean标签的名称找到对应的Controller类
  • SimpleUrlHandlerMapping ,根据bean的id查找对应的Controller类
  • RequestMappingHandlerMapping ,根据controller类的名字找到对应的Controller,这也是我们最常用的

HandlerAdapter会作为一个Bean实例化到Spring 容器,它有以下几种模式:

  • HttpRequestHandlerAdapter,业务自行处理 请求,不需要通过modelAndView 转到视图
  • RequestMappingHandlerAdapter, 基于@RequestMapping对应方法处理 最常用的
  • SimpleControllerHandlerAdapter 标准控制器,返回ModelAndView

首先,我们需要对以上HandlerMapping,HandlerAdapter进行Bean Definition,还是熟悉的味道,

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
106
107
108
109
110
111
112
113
/******DefaultBeanDefinitionDocumentReader.java********/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
/*default namespace 涉及到的就四个标签

* <import />、<alias />、<bean /> 和 <beans />

​ parseDefaultElement(ele, delegate);
​ }
​ else {
​ /*

* 其他的属于 custom(拓展)如我们经常会使用到的
task />、<context />、<aop />等
*/
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
通过NamespaceHandler策略模式,找到NamespaceHandler下接口实现MvcNamespaceHandler

/***MvcNamespaceHandler.java****/
public void init() {
//相同的套路,非默认Spring标签就根据命名空间来找:*NamespaceHandler
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
//...........................................................................
}
进入AnnotationDrivenBeanDefinitionParser解析器,找到解析方法:

/*******AnnotationDrivenBeanDefinitionParser.java********/
public BeanDefinition parse(Element element, ParserContext context) {
//........................................................................
//处理器映射器 RequestMappingHandlerMapping Bean Definition
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);

if (element.hasAttribute("enable-matrix-variables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}

configurePathMatchingProperties(handlerMappingDef, element, context);
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);

RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);

RuntimeBeanReference conversionService = getConversionService(element, source, context);
RuntimeBeanReference validator = getValidator(element, source, context);
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
//绑定Bean Definition,用于适配器处理映射关系查找
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
bindingDef.setSource(source);
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
bindingDef.getPropertyValues().add("conversionService", conversionService);
bindingDef.getPropertyValues().add("validator", validator);
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);

//...................................................................................
//处理器适配器Bean Definition
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
addRequestBodyAdvice(handlerAdapterDef);
addResponseBodyAdvice(handlerAdapterDef);

if (element.hasAttribute("ignore-default-model-on-redirect")) {
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
if (asyncTimeout != null) {
handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
}
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}

handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
//.......................................................

context.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
context.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
//.....................................................
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
//注册MVC组件(处理器映射器,处理器适配器)
MvcNamespaceUtils.registerDefaultComponents(context, source);
context.popAndRegisterContainingComponent();
return null;
}

接下来的事情我们都很清楚了,无非是根据Bean Definition实例化Bean,属性设置,后置处理器回调

而我们对于HandlerMapping,HandlerAdapter处理就在后置处理器中,我们以RequestMappingHandlerMapping为例,将断点打在此处:

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
一直跟进到具体回调方法:

/**************************************AbstractHandlerMethodMapping.java**************************/
//重写的afterPropertiesSet方法
public void afterPropertiesSet() {
initHandlerMethods();
}

protected void initHandlerMethods() {
//遍历所有的Bean Name,进行mapping映射设置
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//跟进去瞅瞅
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}

protected void processCandidateBean(String beanName) {
//............................................................
if (beanType != null && isHandler(beanType)) {
//继续跟进去
detectHandlerMethods(beanName);
}
}

protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());

if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//注册处理器方法,其实就是将往HandlerMapping中设置url和controller的映射
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}

3.初始化 mvc 各个组件

当IOC容器实例完成初始化并设置属性依赖后,回调用finishRefresh方法进行广播,执行publishEvent方法触发监听事件,还记得前面注册到Web容器中的SourceFilteringListener监听器吗?这里会触发onApplicationEvent方法:

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
/*******************************************SourceFilteringListener.java********************/
public void onApplicationEvent(ApplicationEvent event) {
if (event.getSource() == this.source) {
onApplicationEventInternal(event);
}
}

protected void onApplicationEventInternal(ApplicationEvent event) {
if (this.delegate == null) {
throw new IllegalStateException(
"Must specify a delegate object or override the onApplicationEventInternal method");
}
//使用监听器适配器对象(因为有太多的监听类型)进行事件回调处理
//---》请一直跟进到FrameworkServlet,会发现调用了其onRefresh方法进行MVC组件的初始化
this.delegate.onApplicationEvent(event);
}
DispatchServlet继承了FrameworkServlet,重写了onRefresh方法,跟到 initStrategies方法:

protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);//文件上传
initLocaleResolver(context);//国际化
initThemeResolver(context);//动态样式
initHandlerMappings(context);//处理器映射器
initHandlerAdapters(context);//处理器适配器
initHandlerExceptionResolvers(context);//处理器异常解析器
initRequestToViewNameTranslator(context);//请求视图名转换器
initViewResolvers(context);//视图解析器
initFlashMapManager(context);//每一个FlashMap保存着一套Redirect转发所传递的参数
}
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
文件上传解析器

private void initMultipartResolver(ApplicationContext context) {
try {
//初始化获取文件上传解析器,其实就是一个Bean
/*<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">*/
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
//***************************************************************
}
国际化解析器

private void initLocaleResolver(ApplicationContext context) {
try {
//国际化解析器,其实还是一个Bean
//<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
//**********************************
}
动态样式模板解析器

private void initThemeResolver(ApplicationContext context) {
try {
//动态样式模板解析器,还是一个Bean
//<bean id="themeResolver" class="org.springframework.web.servlet.theme.SessionThemeResolver"/>
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
//********************************************************
}
处理器映射器

private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;

if (this.detectAllHandlerMappings) {//处理所有的处理器映射
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 查找ApplicationContext中的所有HandlerMapping
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
//拿到注册的Map<url,controller>处理器映射集合,存入handlerMappings集合
if (!matchingBeans.isEmpty()) {
//处理器映射器放的东西包括(control对象或者control方法和url的映射,还有过滤器调用链)
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 按照优先级排序.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
//.......................................................................
}
处理器适配器

private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;

if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
//.........................................................................
}

4.请求的执行

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
FrameworkServlet重写了父类HttpServletBean的doGet,doPost方法

/************************************FrameworkServlet.java****************************/
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

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

//..........................................
try {
//集中访问入口
doService(request, response);
}
//......................................
}
DispatchServlet又重写了父类doService方法:

/************************************DispatchServlet.java****************************/
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//...............................................
try {
//集中分发访问入口
doDispatch(request, response);
}
//...............................................
}

这也是整个MVC核心方法:

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
/************************************DispatchServlet.java****************************/
/** 中央控制器,控制请求的转发 **/
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.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.
// 这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和interceptors.
mappedHandler = getHandler(processedRequest);
// 如果handler为空,则返回404
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

//3. 获取处理request的处理器适配器handler adapter
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;
}
}
// 4.拦截器的预处理方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 5.实际的处理器处理请求,返回结果视图对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 结果视图对象的处理
applyDefaultViewName(processedRequest, mv);
// 6.拦截器的后处理方法
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);
}
// 请求成功响应之后的方法
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);
}
}
}
}

getHandler(processedRequest)方法实际上就是从HandlerMapping中找到url和controller的对应关系。这也就是之前在初始化MVC组件建立Map<url,Controller>的意义。我们知道,最终处理request的是controller中的方法,我们现在只是知道了controller,还要进一步确认controller中处理request的方法。

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/************************************DispatchServlet.java****************************/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/*处理器映射器(包含handler和intercept)
包含三种:
RequestMappingHandlerMapping 最常见模式
BeanNameUrlHandlerMapping 以 "/" 开头的 bean <bean id="/sayByeBye.do" class="com.ydt.spring03.controller.OrderController"></bean>
SimpleUrlHandlerMapping :<bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="sayByeBye2.do">orderController</prop>
</props>
</property>
</bean>
*/
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//先根据请求获取对应的handler
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//获取处理器执行链(handler,intercept)
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

//.........................................................
}

/************************************RequestMappingInfoHandlerMapping.java****************************/
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
//调用父类AbstractHandlerMethodMapping模板方法
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}

/************************************AbstractHandlerMethodMapping.java****************************/
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//根据request解析得到请求路径:/index.do
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
//根据请求路径和request获取HandlerMethod对象,里面包括bean name,method,参数等
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//再一次通过bean name从容器中取出对应的controller对象
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
//通过遍历所有的mapping key,获取到所有匹配/index.*的映射,添加到matches中
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}

if (!matches.isEmpty()) {
//只取第一个,这也是为什么我们写多个相同controller接口时,只有一个生效的原因
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
//这个地方就是对多个匹配的controller的情况下,给用户一个异常提示
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
//返回HandlerMethod对象
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}

//获取执行链,这个地方主要是获取拦截器,也就是之前MVC组件初始化的时候的adaptedInterceptors,默认会有区域切换和类型转换的拦截器
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//拿到handler(controller或者method)
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//访问路径
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
//判断过滤器是否为mapping过滤器,如果是普通Bean过滤器,直接往执行链中扔
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
//判断访问路径是否在过滤器中被包含或者被排除
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}

(1)过滤器:
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等

(2)拦截器:
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处

获取请求处理适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替Controller执行相应的方法。这样在扩展Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展了

/************************************DispatcherServlet.java****************************/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
/*根据handler(参数)获取处理器适配器
包含三种:
RequestMappingHandlerAdapter 注解处理器适配器(最常用)
HttpRequestHandlerAdapter 支持handler是Servlet的适配模式,也就是实现了HttpRequestHandler的handler
SimpleControllerHandlerAdapter 非注解处理器适配器,支持所有实现了 Controller 接口的 Handler 控制器
*/
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
//......................................................
}

MVC过滤器前置处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
//如果前置处理完成并返回true,触发执行最终处理
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}

反射调用返回结果视图

根据url确定Controller中处理请求的方法,然后通过反射获取该方法上的注解和参数,解析方法和参数上的注解,最后反射调用方法获取ModelAndView结果视图。因为上面采用注解url形式说明的,所以我们这里继续以注解处理器适配器来说明。调用的就是HandlerAdapter的handle(),适配器子类RequestMappingHandlerAdapter.handle()中的核心逻辑由invokeHandlerMethod(request, response, handler)实现。

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
/*********************************RequestMappingHandlerAdapter.java****************************/
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);

// 涉及到Session的情况下对于handler的调用
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 {
// 进入我们的对应的适配器了,跟进去
mav = invokeHandlerMethod(request, response, handlerMethod);
}

//...................................
}

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//..........................一大堆的设置:参数,返回视图,包装好的执行方法........
//核心调度方法,使用反射调用目标对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//通过mavContainer进行视图处理
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

//无非是得到Model和View
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}

//中间是一堆的MVC边角料功能,我们跟着主线一直跟进到
/*****************************InvocableHandlerMethod.java**************************/
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
//底层反射调用目标对象方法(这个地方使用了桥接方法,主要是兼容JDK1.5之前不支持泛型的问题)
return getBridgedMethod().invoke(getBean(), args);
}
//...................................................
}

通过上面的代码,已经可以找到处理request的Controller中的方法了,现在看如何解析该方法上的参数,并调用该方法。也就是执行方法这一步。执行方法这一步最重要的就是获取方法的参数,然后我们就可以反射调用方法了。

SpringMVC中提供两种request参数到方法中参数的绑定方式:
  ① 通过注解进行绑定 @RequestParam
  ② 通过参数名称进行绑定   
  使用注解进行绑定,我们只要在方法参数前面声明@RequestParam(“a”),就可以将request中参数a的值绑定到方法的该参数上。使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法。SpringMVC解决这个问题的方法是用asm框架读取字节码文件,来获取方法的参数名称。个人建议,使用注解来完成参数绑定,这样就可以省去asm框架的读取字节码的操作。

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
/****************************************InvocableHandlerMethod.java*********************/
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}

Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}