Spring @RequestMapping 工作原理
配置基础启动类及Controller类
@SpringBootApplication
public class DemoServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DemoServiceApplication.class, args);
}
}
@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);
}
}
@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 {
// ...
}
请求验证
$ curl -X PUT http://localhost:8080/hello
文章来源:https://www.toymoban.com/news/detail-595141.html
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.
}
}
// ...
}
package org.springframework.web.servlet;
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
// ...
// no override service(), subclass `FrameworkServlet` will implement this method
// ...
}
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;
// ...
}
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
文章来源地址https://www.toymoban.com/news/detail-595141.html
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;
}
// ...
}
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);
// ...
}
// ...
}
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
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
directPathMatches = {ArrayList@7854} size = 2
0 = {RequestMappingInfo@7872} "{PUT [/hello]}"
1 = {RequestMappingInfo@7868} "{PATCH [/hello]}"
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);
}
// ...
}
到了这里,关于Spring @RequestMapping 工作原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!