日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

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

發布時間:2025/3/19 c/c++ 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springmvc path请求映射到bean 方法的流程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、加載注冊流程

? ? ? 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@Overridepublic 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注解,才會處理。

?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。下面對這四個對象進行說明:

  • mappingLookup對象是Map結構,key表示mapping對象,value表示處理對象,在本例中key是RequestMappingInfo對象,value是Controller中的某一個方法。

  • urlLookup對象是Map結構,key表示url,value表示mapping對象,本例中key是具體的url值"/demo/postMapping/",value是RequestMappingInfo對象,

  • corsLookup對象是Map結構,key表示處理方法(Controller中的某個方法),value表示跨域配置,本例中沒有進行跨域注解的使用因此數據不存在,如果需要看到跨域數據,可以在method上添加@CrossOrigin注解

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

    生成的數據如下:

    5.系統攔截器列表初始化過程,會調用到AbstractHandlerMapping.initApplicationContext,這個會查找當前容器工廠中所有繼承了MappedInterceptor類的攔截器實例BEAN.然后保存到AbstractHandlerMapping.interceptors

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


    二、調用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 afterCompletionif (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

    調用堆棧2

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

    這個類會根據攔截器的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;}}

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

    ?

    HandlerMethodReturnValueHandler的初始化流程和參數解析器一樣,也可以自定義。

    ?9.2 由于我們在方法上加了@ResponseBody注解,所有此handler為

    RequestResponseBodyMethodProcessor。此類繼承了AbstractMessageConverterMethodProcessor,這個類會調用當前容器工廠中所有 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;}}}}

    9.3 messageConverts的數據初始化來源為 RequestMappingHandlerAdapter.getDefaultArgumentResolvers方法中, resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));

    messageConverts來源于RequestMappingHandlerAdapter.messageConverters中。

    在初始化時會,依賴注入當前系統中所有的messageConvert.

    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 方法的流程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。