阅读 163

SpringMVC之RequestMappingHandlerMapping

SpringMVC之RequestMappingHandlerMapping

一句话:这个类主要作用之一是将请求url和对应方法的映射关系缓存起来,方便后续快速路由请求到指定的method


原理

RequestMappingHandlerMapping注入时机

DispatcherServlet是一个servlet,它的生命周期受容器控制,即容器在servlet实例化后会调用对应的init方法进行初始化(实际是执行父类 org.springframework.web.servlet.HttpServletBean 的init方法)



方法会执行一系列的初始化动作


这里我们分析一下


org.springframework.web.servlet.DispatcherServlet#initHandlerMappings

1

这个方法,因为这个方法会注入我们今天的主角类;下面这段代码会将默认的策略类组合赋值给 DispatcherServlet 相应的属性



RequestMappingHandlerMapping分析

首先来看下RequestMappingHandlerMapping的继承关系图


这里我最关心也是最重要的一点,它是一个 InitializingBean !!!

这很重要,这表明在类实例化后会自动执行它的 afterPropertiesSet 方法,调用链如下:


org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet


@Override

public void afterPropertiesSet() {

this.config = new RequestMappingInfo.BuilderConfiguration();

this.config.setUrlPathHelper(getUrlPathHelper());

this.config.setPathMatcher(getPathMatcher());

this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);

this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);

this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);

this.config.setContentNegotiationManager(getContentNegotiationManager());


// @A

super.afterPropertiesSet();

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@A:调用父类方法即org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet

@Override

public void afterPropertiesSet() {

//@B

initHandlerMethods();

}


/**

* Scan beans in the ApplicationContext, detect and register handler methods.

* @see #isHandler(Class)

* @see #getMappingForMethod(Method, Class)

* @see #handlerMethodsInitialized(Map)

*/

protected void initHandlerMethods() {

if (logger.isDebugEnabled()) {

logger.debug("Looking for request mappings in application context: " + getApplicationContext());

}

//@C

String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?

BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :

obtainApplicationContext().getBeanNamesForType(Object.class));

//@D

for (String beanName : beanNames) {

if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {

Class<?> beanType = null;

try {

beanType = obtainApplicationContext().getType(beanName);

}

catch (Throwable ex) {

// An unresolvable bean type, probably from a lazy bean - let's ignore it.

if (logger.isDebugEnabled()) {

logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);

}

}

//@E

if (beanType != null && isHandler(beanType)) {

detectHandlerMethods(beanName);

}

}

}

handlerMethodsInitialized(getHandlerMethods());

}

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

@B:调用 initHandlerMethods 方法


@C:根据是否搜索parent容器(false)获得容器中所有的类;(因为这是springmvc项目,只需要搜索当前springmvc容器定义的所有类,准确的说后续只需要接卸controller类)


@D:循环遍历beaname获得controller bean


@E:执行 isHandler 方法(被 @Controller注解或者被 @RequestMapping注解标注的类),根据返回值决定是否解析类里面的方法


下面我们来到解析url和方法的具体方法 detectHandlerMethods


protected void detectHandlerMethods(final Object handler) {

//@F

Class<?> handlerType = (handler instanceof String ?

obtainApplicationContext().getType((String) handler) : handler.getClass());


if (handlerType != null) {

final Class<?> userType = ClassUtils.getUserClass(handlerType);

//@G

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.isDebugEnabled()) {

logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);

}

//@H

methods.forEach((method, mapping) -> {

Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);

registerHandlerMethod(handler, invocableMethod, mapping);

});

}

}


/**

* Register a handler method and its unique mapping. Invoked at startup for

* each detected handler method.

* @param handler the bean name of the handler or the handler instance

* @param method the method to register

* @param mapping the mapping conditions associated with the handler method

* @throws IllegalStateException if another method was already registered

* under the same mapping

*/

protected void registerHandlerMethod(Object handler, Method method, T mapping) {

//@I

this.mappingRegistry.register(mapping, handler, method);

}

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

@F:获得controller类 Class


@G:解析controller获得一个Map<Method,RequestMappingInfo>的映射关系,具体封装RequestMappingInfo是子类 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod方法


@H:循环遍历找到的controller方法,缓存url和方法的关系


@I:mapping是RequestMappingInfo对象;handler是controller的类名称(小写开头);method是被@RequestMapping注解标记的方法


MappingRegistry是 AbstractHandlerMethodMapping的内部类,我们看看register方法


public void register(T mapping, Object handler, Method method) {

//@J

this.readWriteLock.writeLock().lock();

try {

//@K

HandlerMethod handlerMethod = createHandlerMethod(handler, method);

assertUniqueMethodMapping(handlerMethod, mapping);


if (logger.isInfoEnabled()) {

logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);

}

//@L

this.mappingLookup.put(mapping, handlerMethod);

//@M

List<String> directUrls = getDirectUrls(mapping);

for (String url : directUrls) {

this.urlLookup.add(url, mapping);

}


String name = null;

if (getNamingStrategy() != null) {

name = getNamingStrategy().getName(handlerMethod, mapping);

addMappingName(name, handlerMethod);

}


CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);

if (corsConfig != null) {

this.corsLookup.put(handlerMethod, corsConfig);

}

//@N

this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));

}

finally {

this.readWriteLock.writeLock().unlock();

}

}

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

@J:获得一个写锁

@K:根据类名和方法封装成一个HandlerMethod对象

@L:放入RequestMappingInfo为key的map中

@M:获得方法上声明的url集合,因为 @RequestMapping 注解可以指定多个url;然后缓存到url为key的LinkedMultiValueMap中

@N:缓存进以RequestMappingInfo为key的 HashMap 中,value是<RequestMappingInfo,方法,注解上标注的url集合,根据策略处理后的方法名>的封装对象

RequestMappingHandlerMapping方法路由

一句话:根据J2EE规范处理http请求的方法是 DispatcherServlet的doService方法,核心方法是org.springframework.web.servlet.DispatcherServlet#doDispatch(这个方法是被父类的doService方法调起)

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);


//@A

// Determine handler for the current request.

mappedHandler = getHandler(processedRequest);

if (mappedHandler == null) {

noHandlerFound(processedRequest, response);

return;

}


// Determine handler adapter for the current request.

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 (logger.isDebugEnabled()) {

logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);

}

if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {

return;

}

}

//@A1

if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;

}

//@B

// Actually invoke the handler.

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


if (asyncManager.isConcurrentHandlingStarted()) {

return;

}

//@C

applyDefaultViewName(processedRequest, mv);

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);

}


//@D

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);

}

}

}

}

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

@A:根据HttpServletRequest请求对象获得一个 HandlerExecutionChain对象(实际上是对匹配到的HandlerMethod的封装),具体逻辑在org.springframework.web.servlet.DispatcherServlet#getHandler方法,下面我们具体分析


@A1:执行拦截器逻辑,这里可见拦截器是在执行真正方法前被执行


@B:真正执行HandleMethod的方法,这里是委托给HandlerAdapter(真实实现类是:RequestMappingHandlerAdapter)来执行的,这里会返回一个 ModelAndView


@C:设置view的名称,执行一些后置处理


@D:处理结果,渲染界面(渲染界面逻辑下一篇文章详细揭秘)


接上面@A里分析org.springframework.web.servlet.DispatcherServlet#getHandler方法如何获得HandlerExecutionChain对象


@Nullable

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

if (this.handlerMappings != null) {

for (HandlerMapping hm : this.handlerMappings) {

if (logger.isTraceEnabled()) {

logger.trace(

"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");

}

HandlerExecutionChain handler = hm.getHandler(request);

if (handler != null) {

return handler;

}

}

}

return null;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

这个方法逻辑是:循环遍历 handlerMappings,然后调用getHandler方法,如果不为空的话就返回;根据前面的内容我们知道 handlerMappings是2个对象(怎么少了一个?)

+

这里其实起作用的就是上面框架注入的 RequestMappingHandlerMapping,getHandler方法是在它的父类org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler里实现的;

具体调用栈如下:


org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler

|

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal(根据url找找到之前缓存的元数据信息并封装成 HandlerMethod 对象)

|

org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain(将HandlerMethod封装成HandlerExecutionChain对象并返回)

|

1

2

3

4

5

6

最终返回一个 HandlerExecutionChain 对象,传给@B逻辑

————————————————

版权声明:本文为CSDN博主「万物皆字节」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/Aqu415/article/details/116175042


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐