springmvc path请求映射到bean 方法的流程

作者:乘风破浪的码农 时间:2021-10-25 04:42:58 

一、加载注册流程

1.在dispatch-servlet.xml中配置< mvc:annotation-driven/>,在控制器的方法上加入@RequestMapping注解即可。

2.mvc:annotation-driven的解析流程 会调用到自定义元素解析器的AnnotationDrivenBeanDefinitionParser.parse方法。

3.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping为RequestMapping注解映射到后台接口的注册表。此类实现了InitializingBean接口,会触发到

afterPropertiesSet方法。


org.springframework.web.servlet.handler.AbstractHandlerMethodMapping

@Override
public void afterPropertiesSet() {
initHandlerMethods();
}

/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}

4.在initHandlerMethods方法中会先调用getCandidateBeanNames获取当前容器工厂的所有BEAN,然后逐个BEAN进行处理。

4.1 获取所有BEAN流程


protected String[] getCandidateBeanNames() {
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
}

4.2 处理BEAN流程


protected void processCandidateBean(String beanName) {
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.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}

4.3 判断当前BEAN是否HANDLER


protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

4.4 如果此类是控制器或者有requestMapping注解,才会处理。

springmvc path请求映射到bean 方法的流程

4.5 遍历当前类的所有方法,查找包含RequestMapping注解的方法,然后保存到

AbstractHandlerMethodMapping.MappingRegistry注册表中。


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);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}

判断当前方法是否包含requestMapping注解


private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

最终会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod保存到URL和RequestMappinfo的映射注册表中。


AbstractHandlerMethodMapping
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}

具体的保存逻辑

在这个方法中主要操作的数据对象有四个,分别是mappingLookup、urlLookup、corsLookup和registry。下面对这四个对象进行说明:

  1. mappingLookup对象是Map结构,key表示mapping对象,value表示处理对象,在本例中key是RequestMappingInfo对象,value是Controller中的某一个方法。

  2. urlLookup对象是Map结构,key表示url,value表示mapping对象,本例中key是具体的url值"/demo/postMapping/",value是RequestMappingInfo对象,

  3. corsLookup对象是Map结构,key表示处理方法(Controller中的某个方法),value表示跨域配置,本例中没有进行跨域注解的使用因此数据不存在,如果需要看到跨域数据,可以在method上添加@CrossOrigin注解

  4. registry对象是Map结构,key表示mapping对象,value表示MappingRegistration对象


AbstractHandlerMethodMapping.MappingRegistry  内部类
public void register(T mapping, Object handler, Method method) {

this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
this.mappingLookup.put(mapping, handlerMethod);

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

this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}

生成的数据如下:

springmvc path请求映射到bean 方法的流程

5.系统 * 列表初始化过程,会调用到AbstractHandlerMapping.initApplicationContext,这个会查找当前容器工厂中所有继承了MappedInterceptor类的 * 实例BEAN.然后保存到AbstractHandlerMapping.interceptors


protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}

springmvc path请求映射到bean 方法的流程

二、调用HTTP请求根据PATH寻找接口方法流程

1.首先tomcat会调用DispatcherServlet.doDispatch方法,进行请求分发处理。


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

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

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

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

2.首先调用getHandler去根据请求PATH查找HandlerExecutionChain,HandlerExecutionChain就是一个RequestHandleMappinfo加上一个 * 列表。会调用到AbstractHandlerMapping.getHandler


public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
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);
}

HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

return executionChain;
}

3.最终会调用到AbstractHandlerMapping.lookupHandlerMethod根据PATH查找HandlerMethod,这里面的 this.mappingRegistry.getMappingsByUrl就是初始化时的URL和RequestMappingInfo映射表。


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()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}

if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}

调用堆栈1

springmvc path请求映射到bean 方法的流程

调用堆栈2

springmvc path请求映射到bean 方法的流程 4.

初始化HandlerExecutionChain * 列表,这个会查找当前容器工厂中所有实现了handleInteropr 的类,


AbstractHandlerMapping类
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}

springmvc path请求映射到bean 方法的流程

这个类会根据 * 的URL匹配规则相应添加 * 列表。


<mvc:interceptors>
<bean class="com.tpw.component.HandlerInterceptor1"></bean>
   <mvc:interceptor>
       <mvc:mapping path="/user"/>
       <bean class="com.tpw.component.HandlerInterceptor2"></bean>
   </mvc:interceptor>
</mvc:interceptors>

5.调用所有 * 的applyPreHandle方法


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];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}

7.然后调用invocableMethod.invokeAndHandle(webRequest, mavContainer),首先通过反射调用handlerMethod中的bean的接口方法


ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

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

if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}

mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}

springmvc path请求映射到bean 方法的流程

9.最后调用HandlerMethodReturnValueHandlerComposite.handleReturnValue 进行返回值处理,例如将BEAN转JSON,转XML等。

9.1 这个找HANDLER的过程也是,根据此HANDLER是否支持此方法,如在方法上加上了@ResponseBody,则会由RequestResponseBodyMethodProcessor处理。


RequestResponseBodyMethodProcessor
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

springmvc path请求映射到bean 方法的流程


RequestResponseBodyMethodProcessor。
此类继承了AbstractMessageConverterMethodProcessor,这个类会调用当前容器工厂中所有

springmvc path请求映射到bean 方法的流程

9.2 由于我们在方法上加了@ResponseBody注解,所有此handler为


protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

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

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

if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}

}

springmvc path请求映射到bean 方法的流程

9.3 messageConverts的数据初始化来源为 RequestMappingHandlerAdapter.getDefaultArgumentResolvers方法中,


resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));

messageConverts来源于RequestMappingHandlerAdapter.messageConverters中。

在初始化时会,依赖注入当前系统中所有的messageConvert.

springmvc path请求映射到bean 方法的流程

10.最后调用 * 的所有postHandle方法进行,处理完回调。


void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {

HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}

11.在渲染完输出视图后,会调用所有 * 的afterCompletion方法,注意,JSON,XML这种没有视图,只有HTML等才有。


void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {

HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}

springmvc path请求映射到bean 方法的流程

来源:https://blog.csdn.net/lipengyao2010/article/details/118941496

标签:springmvc,请求,映射
0
投稿

猜你喜欢

  • Android使用httpPost向服务器发送请求的方法

    2021-12-08 19:37:07
  • Java实现pdf转图片案例

    2022-08-11 21:45:41
  • ToStringBuilder类的一些心得

    2022-10-10 04:02:27
  • C#中添加窗口的步骤详解

    2021-12-19 16:30:29
  • spring框架cacheAnnotation缓存注释声明解析

    2022-04-14 17:13:05
  • 在.net应用程序中运行其它EXE文件的方法

    2023-11-08 08:34:43
  • java编写简单的ATM存取系统

    2023-06-28 07:50:33
  • Android实现文件下载进度显示功能

    2023-12-26 00:42:28
  • java基础的详细了解第四天

    2022-11-25 07:24:51
  • Android开发入门环境快速搭建实战教程

    2022-06-17 15:59:41
  • JAVA中数组从小到大排序的2种方法实例

    2021-10-09 09:46:18
  • JAVA遍历map的几种实现方法代码

    2023-08-27 22:06:04
  • Java环境下高德地图Api的使用方式

    2022-06-13 06:43:59
  • 浅谈hibernate急迫加载问题(多重外键关联)

    2023-08-18 12:58:54
  • 使用Spring Data JDBC实现DDD聚合的示例代码

    2022-05-04 05:11:23
  • 解析android中的dip,dp,px,sp和屏幕密度

    2023-09-26 20:44:38
  • 简单了解Java方法的定义和使用实现详解

    2023-10-30 16:12:46
  • Android自定义控件之组合控件学习笔记分享

    2022-09-18 01:09:22
  • SpringCloud分布式链路追踪组件Sleuth配置详解

    2023-11-28 23:58:42
  • java实现超市库存管理系统

    2022-06-28 01:23:44
  • asp之家 软件编程 m.aspxhome.com