Spring @RequestMapping 工作原理

Spring @RequestMapping 工作原理

配置基础启动类及Controller类

java 复制代码
@SpringBootApplication
public class DemoServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoServiceApplication.class, args);
    }
}
java 复制代码
@RestController
public class HelloController {

    @PatchMapping("/hello")
    // above statement is a shortcut for below statement
    // @RequestMapping(value = "/hello", method = RequestMethod.PATCH)
    public ResponseData hello() {
        return ResponseData.success(null);
    }

    @PutMapping ("/hello")
	// above statement is a shortcut for below statement
	// @RequestMapping(value = "/hello", method = RequestMethod.PUT)
    public ResponseData hello2() {
        return ResponseData.success(null);
    }
}
java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.PATCH)
public @interface PatchMapping {
	// ...
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.PUT)
public @interface PutMapping {
	// ...
}

请求验证

bash 复制代码
$ curl -X PUT http://localhost:8080/hello
java 复制代码
package jakarta.servlet.http;

public abstract class HttpServlet extends GenericServlet {
	// ...
	
	/**
	 * Dispatches client requests to the protected service method. There's no need to override this method.
	 * ...
	 */
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

        HttpServletRequest request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        service(request, response);
    }
    
    // ...
    
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
        	// ..
	        doGet(req, resp);
            // ...
        } else if (method.equals(METHOD_HEAD)) {
        	// ..
	        doHead(req, resp);
        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req, resp);
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req, resp);
        } else {
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
        }
    }
    // ...
}
java 复制代码
package org.springframework.web.servlet;

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
	// ...
	// no override service(), subclass `FrameworkServlet` will implement this method
	// ...
}
java 复制代码
package org.springframework.web.servlet;

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

	// ...
	private static final Set<String> HTTP_SERVLET_METHODS =
			Set.of("DELETE", "HEAD", "GET", "OPTIONS", "POST", "PUT", "TRACE");
	// ...
	
	/**
	 * Override the parent class implementation in order to intercept requests
	 * using PATCH or non-standard HTTP methods (WebDAV).
	 */
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		if (HTTP_SERVLET_METHODS.contains(request.getMethod())) {
			super.service(request, response);
		} // PATCH is missing, so process will go here
		else {
			processRequest(request, response);
		}
	}
	// ...

	@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Delegate POST requests to {@link #processRequest}.
	 * @see #doService
	 */
	@Override
	protected final void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Delegate PUT requests to {@link #processRequest}.
	 * @see #doService
	 */
	@Override
	protected final void doPut(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Delegate DELETE requests to {@link #processRequest}.
	 * @see #doService
	 */
	@Override
	protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Delegate OPTIONS requests to {@link #processRequest}, if desired.
	 * <p>Applies HttpServlet's standard OPTIONS processing otherwise,
	 * and also if there is still no 'Allow' header set after dispatching.
	 * @see #doService
	 */
	@Override
	protected void doOptions(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
			processRequest(request, response);
			if (response.containsHeader(HttpHeaders.ALLOW)) {
				// Proper OPTIONS response coming from a handler - we're done.
				return;
			}
		}

		// Use response wrapper in order to always add PATCH to the allowed methods
		super.doOptions(request, new HttpServletResponseWrapper(response) {
			@Override
			public void setHeader(String name, String value) {
				if (HttpHeaders.ALLOW.equals(name)) {
					value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
				}
				super.setHeader(name, value);
			}
		});
	}

	/**
	 * Delegate TRACE requests to {@link #processRequest}, if desired.
	 * <p>Applies HttpServlet's standard TRACE processing otherwise.
	 * @see #doService
	 */
	@Override
	protected void doTrace(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		if (this.dispatchTraceRequest) {
			processRequest(request, response);
			if ("message/http".equals(response.getContentType())) {
				// Proper TRACE response coming from a handler - we're done.
				return;
			}
		}
		super.doTrace(request, response);
	}

	/**
	 * Process this request, publishing an event regardless of the outcome.
	 * <p>The actual event handling is performed by the abstract
	 * {@link #doService} template method.
	 */
	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		try {
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new ServletException("Request processing failed: " + ex, ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}
	
	// ...
	protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
	// ...
}
java 复制代码
package org.springframework.web.servlet;

public class DispatcherServlet extends FrameworkServlet {
	// ...
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// ...
		try {
			doDispatch(request, response);
		}
		finally {
			// ...
		}
		// ...
	}
	
	// ...
	
	/**
	 * Process the actual dispatching to the handler.
	 * The handler will be obtained by applying the servlet's HandlerMappings in order. 
	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters to find the first that supports the handler class. 
	 * All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers themselves to decide which methods are acceptable.
	 */	 
	@SuppressWarnings("deprecation")
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// ...
		try {
		// ...
			try {
				// ...
				// Determine handler for the current request.
				// important here
				mappedHandler = getHandler(processedRequest);
				// ...
				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				// ...
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			// catch()
		} 
		//catch()
	}
	
	// ...
	
	/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 */
	@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;
	}

	// ...
}		
复制代码
this.handlerMappings = {ArrayList@7649}  size = 5
	 0 = {RequestMappingHandlerMapping@6913} 
	 1 = {BeanNameUrlHandlerMapping@7828} 
	 2 = {RouterFunctionMapping@7829} 
	 3 = {SimpleUrlHandlerMapping@7830} 
	 4 = {WelcomePageHandlerMapping@7831} 

展开 0 = {RequestMappingHandlerMapping@6913}

复制代码
 mappingRegistry = {AbstractHandlerMethodMapping$MappingRegistry@6921} 
	 registry = {HashMap@7840}  size = 5
		  {RequestMappingInfo@7864} "{GET [/api/tutorials]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7865} 
		  {RequestMappingInfo@7866} "{ [/error], produces [text/html]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7867} 
		  {RequestMappingInfo@7868} "{PATCH [/hello]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7869} 
		  {RequestMappingInfo@7870} "{ [/error]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7871} 
		  {RequestMappingInfo@7872} "{PUT [/hello]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7873} 
	 pathLookup = {LinkedMultiValueMap@7841}  size = 3
		  "/api/tutorials" -> {ArrayList@7852}  size = 1
		  "/hello" -> {ArrayList@7854}  size = 2
		  "/error" -> {ArrayList@7856}  size = 2
java 复制代码
package org.springframework.web.servlet.handler;

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
		implements HandlerMapping, Ordered, BeanNameAware {
	// ...
	
	/**
	 * Look up a handler for the given request, falling back to the default
	 * handler if no specific one is found.
	 * @param request current HTTP request
	 * @return the corresponding handler instance, or the default handler
	 * @see #getHandlerInternal
	 */
	@Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerzInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String handlerName) {
			handler = obtainApplicationContext().getBean(handlerName);
		}

		// Ensure presence of cached lookupPath for interceptors and others
		if (!ServletRequestPathUtils.hasCachedPath(request)) {
			initLookupPath(request);
		}

		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}

		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
			CorsConfiguration config = getCorsConfiguration(handler, request);
			if (getCorsConfigurationSource() != null) {
				CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
				config = (globalConfig != null ? globalConfig.combine(config) : config);
			}
			if (config != null) {
				config.validateAllowCredentials();
			}
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

	// ...
}
java 复制代码
package org.springframework.web.servlet.mvc.method;
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
	// ..
	
	@Override
	@Nullable
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		// ...
		return super.getHandlerInternal(request);
		// ...
	}
	
	// ...	
}
java 复制代码
package org.springframework.web.servlet.handler;

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
	// ...
	
	@Override
	@Nullable
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = initLookupPath(request);
		this.mappingRegistry.acquireReadLock();
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
	// ...

	@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
		}
		if (!matches.isEmpty()) {
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
				matches.sort(comparator);
				bestMatch = matches.get(0);
				if (logger.isTraceEnabled()) {
					logger.trace(matches.size() + " matching mappings: " + matches);
				}
				if (CorsUtils.isPreFlightRequest(request)) {
					for (Match match : matches) {
						if (match.hasCorsConfig()) {
							return PREFLIGHT_AMBIGUOUS_MATCH;
						}
					}
				}
				else {
					Match secondBestMatch = matches.get(1);
					if (comparator.compare(bestMatch, secondBestMatch) == 0) {
						Method m1 = bestMatch.getHandlerMethod().getMethod();
						Method m2 = secondBestMatch.getHandlerMethod().getMethod();
						String uri = request.getRequestURI();
						throw new IllegalStateException(
								"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
					}
				}
			}
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.getHandlerMethod();
		}
		else {
			return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
		}
	}
	// ...
}

查看looupPath

复制代码
lookupPath="/hello"

this.mappingRegistry = {AbstractHandlerMethodMapping$MappingRegistry@6921} 
	 registry = {HashMap@7840}  size = 5
		  {RequestMappingInfo@7864} "{GET [/api/tutorials]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7865} 
		  {RequestMappingInfo@7866} "{ [/error], produces [text/html]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7867} 
		  {RequestMappingInfo@7868} "{PATCH [/hello]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7869} 
		  {RequestMappingInfo@7870} "{ [/error]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7871} 
		  {RequestMappingInfo@7872} "{PUT [/hello]}" -> {AbstractHandlerMethodMapping$MappingRegistration@7873} 
	 pathLookup = {LinkedMultiValueMap@7841}  size = 3
		  "/api/tutorials" -> {ArrayList@7852}  size = 1
		  "/hello" -> {ArrayList@7854}  size = 2
		  "/error" -> {ArrayList@7856}  size = 2

directPathMatches

bash 复制代码
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
复制代码
directPathMatches = {ArrayList@7854}  size = 2
	 0 = {RequestMappingInfo@7872} "{PUT [/hello]}"
	 1 = {RequestMappingInfo@7868} "{PATCH [/hello]}"
java 复制代码
package org.springframework.web.method;

public class HandlerMethod {
	// ...
	public HandlerMethod createWithResolvedBean() {
		Object handler = this.bean;
		// default would go here
		if (this.bean instanceof String beanName) {
			Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
			handler = this.beanFactory.getBean(beanName);
		}
		return new HandlerMethod(this, handler);
	}
	// ...
}
相关推荐
疯狂的程序猴几秒前
iOS App 混淆的真实世界指南,从构建到成品 IPA 的安全链路重塑
后端
旷野说7 分钟前
为什么 MyBatis 原生二级缓存“难以修复”?
java·java-ee·mybatis
8***235510 分钟前
【wiki知识库】07.用户管理后端SpringBoot部分
java
bcbnb12 分钟前
iOS 性能测试的工程化方法,构建从底层诊断到真机监控的多工具测试体系
后端
开心就好202515 分钟前
iOS 上架 TestFlight 的真实流程复盘 从构建、上传到审核的团队协作方式
后端
小周在成长23 分钟前
Java 泛型支持的类型
后端
aiopencode23 分钟前
Charles 抓不到包怎么办?HTTPS 抓包失败、TCP 数据流异常与底层补抓方案全解析
后端
阿蔹27 分钟前
JavaWeb-Selenium 配置以及Selenim classnotfound问题解决
java·软件测试·python·selenium·测试工具·自动化
稚辉君.MCA_P8_Java28 分钟前
Gemini永久会员 C++返回最长有效子串长度
开发语言·数据结构·c++·后端·算法
Penge6661 小时前
Redis-bgsave浅析
redis·后端