请求处理
RESTful 风格
1、@RequestMapping
(1)支持 RESTful 风格:使用 HTTP 请求方式动词来表示对资源的操作
(2)核心 Filter:HiddenHttpMethodFilter
(3)派生注解:@GetMapping、@PostMapping、@PutMapping、@DeleteMapping
2、HTML
(1)需要表单发送 POST 请求
(2)form 标签,method 属性值为 post
(3)隐藏域,_method 属性值为请求:put、delete、patch 之一(不区分大小写)
(4)put 示例
<form method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="提交put请求">
</form>
3、WebMvcAutoConfiguration
@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebMvcAutoConfiguration {
@Bean
//容器中若没有手动注入的HiddenHttpMethodFilter,会自动配置
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
//判断spring.mvc.hiddenmethod.filter.enabled,是否开启REST
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled")
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
}
(1)spring.mvc.hiddenmethod.filter.enabled 默认为 false
{
"name": "server.compression.enabled",
"type": "java.lang.Boolean",
"description": "Whether response compression is enabled.",
"sourceType": "org.springframework.boot.web.server.Compression",
"defaultValue": false
}
(2)需要在 Spring Boot 的 yaml 配置文件中,手动开启页面表单的 Rest 功能
spring:
mvc:
hiddenmethod:
filter:
enabled: true
4、原理
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
//兼容put、delete、patch请求
private static final List<String> ALLOWED_METHODS =
Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = DEFAULT_METHOD_PARAM;
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//request是当前被拦截的请求
HttpServletRequest requestToUse = request;
//为POST请求时,进入if
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
//paramValue为_method属性的值
String paramValue = request.getParameter(this.methodParam);
//paramValue不为空 / 长度不为0,进入if
if (StringUtils.hasLength(paramValue)) {
//paramValue转换大写,赋值method
String method = paramValue.toUpperCase(Locale.ENGLISH);
//请求若为PUT、DELETE、PATCH之一,进入if
if (ALLOWED_METHODS.contains(method)) {
//由POST请求,转换为其他请求
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
//原生Request,通过装饰,进入过滤链
filterChain.doFilter(requestToUse, response);
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
//原生Request(post)通过装饰器模式,包装为其他请求
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
@Override
public String getMethod() {
return this.method;
}
}
}
请求映射
1、HttpServlet(原生 Servlet)的子类需要重写 doXxx、service 方法
public abstract class HttpServlet extends GenericServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) {
doGet(req, resp);
} else {
NoBodyResponse response = new NoBodyResponse(resp);
doGet(req, response);
if (req.isAsyncStarted()) {
req.getAsyncContext().addListener(new NoBodyAsyncContextListener(response));
} else {
response.setContentLength();
}
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_put_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_delete_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Method[] methods = getAllDeclaredMethods(this.getClass());
boolean ALLOW_GET = false;
boolean ALLOW_HEAD = false;
boolean ALLOW_POST = false;
boolean ALLOW_PUT = false;
boolean ALLOW_DELETE = false;
boolean ALLOW_TRACE = true;
boolean ALLOW_OPTIONS = true;
// Tomcat specific hack to see if TRACE is allowed
Class<?> clazz = null;
try {
clazz = Class.forName("org.apache.catalina.connector.RequestFacade");
Method getAllowTrace = clazz.getMethod("getAllowTrace", (Class<?>[]) null);
ALLOW_TRACE = ((Boolean) getAllowTrace.invoke(req, (Object[]) null)).booleanValue();
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// Ignore. Not running on Tomcat. TRACE is always allowed.
}
// End of Tomcat specific hack
for (int i=0; i<methods.length; i++) {
Method m = methods[i];
if (m.getName().equals("doGet")) {
ALLOW_GET = true;
ALLOW_HEAD = true;
}
if (m.getName().equals("doPost")) {
ALLOW_POST = true;
}
if (m.getName().equals("doPut")) {
ALLOW_PUT = true;
}
if (m.getName().equals("doDelete")) {
ALLOW_DELETE = true;
}
}
String allow = null;
if (ALLOW_GET) {
allow=METHOD_GET;
}
if (ALLOW_HEAD) {
if (allow==null) {
allow=METHOD_HEAD;
} else {
allow += ", " + METHOD_HEAD;
}
}
if (ALLOW_POST) {
if (allow==null) {
allow=METHOD_POST;
} else {
allow += ", " + METHOD_POST;
}
}
if (ALLOW_PUT) {
if (allow==null) {
allow=METHOD_PUT;
} else {
allow += ", " + METHOD_PUT;
}
}
if (ALLOW_DELETE) {
if (allow==null) {
allow=METHOD_DELETE;
} else {
allow += ", " + METHOD_DELETE;
}
}
if (ALLOW_TRACE) {
if (allow==null) {
allow=METHOD_TRACE;
} else {
allow += ", " + METHOD_TRACE;
}
}
if (ALLOW_OPTIONS) {
if (allow==null) {
allow=METHOD_OPTIONS;
} else {
allow += ", " + METHOD_OPTIONS;
}
}
resp.setHeader("Allow", allow);
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int responseLength;
String CRLF = "\r\n";
StringBuilder buffer =
new StringBuilder("TRACE ").append(req.getRequestURI()).append(" ").append(req.getProtocol());
Enumeration<String> reqHeaderEnum = req.getHeaderNames();
while( reqHeaderEnum.hasMoreElements() ) {
String headerName = reqHeaderEnum.nextElement();
buffer.append(CRLF).append(headerName).append(": ")
.append(req.getHeader(headerName));
}
buffer.append(CRLF);
responseLength = buffer.length();
resp.setContentType("message/http");
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
out.print(buffer.toString());
out.close();
}
//重写GenericServlet的service方法
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
//重载
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求方式
String method = req.getMethod();
//get请求,调用doGet
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
//head请求,调用doHead
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
//post请求,调用doPost
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
//put请求,调用doPut
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
//delete请求,调用doDelete
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
//options请求,调用doOptions
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
//trace请求,调用doTrace
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
//服务器上没有支持此请求的方法
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
}
2、HttpServletBean 没有重写 HttpServlet 的方法
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware
3、FrameworkServlet 重写 HttpServlet 中的 service() 和doXxx()
(1)以上方法中调用 processRequest
(2)processRequest 又调用 doService
(3)FrameworkServlet 子类需要实现 doService(空方法)
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
//请求为patch或null
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
//请求为非patch、非null,本类处理get、post、put、delete、options、trace请求,HttpServlet类处理head请求
else {
super.service(request, response);
}
}
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
processRequest(request, response);
if (response.containsHeader("Allow")) {
// Proper OPTIONS response coming from a handler - we're done.
return;
}
}
super.doOptions(request, new HttpServletResponseWrapper(response) {
@Override
public void setHeader(String name, String value) {
if ("Allow".equals(name)) {
value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
}
super.setHeader(name, value);
}
});
}
@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);
}
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 NestedServletException("Request processing failed", 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;
}
4、DispatcherServlet 重写 FrameworkServlet 的 doService
(1)doService 调用 doDispatch
(2)SpringMVC 功能都从 doDispatch 开始
public class DispatcherServlet extends FrameworkServlet {
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
if (this.parseRequestPath) {
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
}
//传入原生HttpServletRequest、HttpServletResponse
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
//是否为文件上传请求,默认为false
boolean multipartRequestParsed = false;
//请求期间是否有异步,若存在,使用异步管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查是否为文件上传请求
processedRequest = checkMultipart(request);
//若为文件上传请求,则为true
multipartRequestParsed = (processedRequest != request);
/*
获取可以处理当前请求的Handler
mappedHandler:调用链,包含handler、interceptorList、interceptorIndex
handler:浏览器发送的请求所匹配的控制器方法
interceptorList:处理控制器方法的所有拦截器集合
interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//调用拦截器的preHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//调用拦截器的postHandle()
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//后续处理:处理模型数据和渲染视图
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()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//处理器映射集合不为null
if (this.handlerMappings != null) {
//遍历handlerMappings,查看HandlerMapping是否有请求信息
for (HandlerMapping mapping : this.handlerMappings) {
//所有的请求映射都在HandlerMapping,包含与Handler映射规则,从HandlerMapping,取得与请求匹配的Handler
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
}
(3)跳入 getHandler
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {
@Override
@Nullable
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);
}
// 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;
}
}
(4)跳入 getHandlerInternal
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
}
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<>();
//根据访问路径,忽略请求方式,从mappingRegistry查找请求
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
//directPathMatches不为null,根据请求方式,再次筛选匹配
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);
//SpringMVC要求,同一个请求,相同请求方式,只能由一个方法处理
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);
}
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
}
}
}
}
5、可以自定义 HandlerMapping,注入 Spring 容器
SpringMVC 处理 Web 请求,可接受的参数类型
1、注解
(1)@PathVariable(路径变量)
该注解表明方法参数应该被绑定到 URI 模板变量
支持 @RequestMapping 的处理方法
如果方法参数是 Map<String, String>,那么该 Map 将被填充所有路径变量的名称和值
(2)@RequestHeader(获取请求头)
该注解表明方法参数应与 Web 请求头绑定
支持 Spring MVC 和 Spring WebFlux 的注解处理方法
如果方法参数是 Map<String, String>, MultiValueMap<String, String>, 或 HttpHeaders,那么该 Map 将被填充所有头的名称和值
(3)@RequestAttribute(获取 Request 域属性)
将一个方法参数绑定到一个请求属性的注解
主要的动机是通过一个可选的 / 必需的检查和对目标方法参数类型的转换,从控制器方法中,提供对请求属性的方便访问
(4)@RequestParam(获取请求参数)
表示方法参数应与 Web 请求参数绑定的注解
在 Spring MVC 和 Spring WebFlux 中,对注解的处理方法支持如下
在 Spring MVC 中,"request parameters "映射到查询参数、表单数据以及多部分请求中的部分。这是因为 Servlet API 将查询参数和表单数据合并到一个名为 "parameters "的单一映射中,这包括对请求主体的自动解析
在 Spring WebFlux 中,"request parameters "只映射到查询参数。为了处理所有3种情况,查询、表单数据和多部分数据,你可以使用数据绑定到用 ModelAttribute 注释的命令对象
如果方法参数类型是 Map,并且指定了请求参数名称,那么假设有合适的转换策略,请求参数值将被转换为 Map
如果方法参数是 Map<String, String> 或 MultiValueMap<String, String>,并且没有指定参数名称,那么 Map 参数将被填充所有请求参数的名称和值
(5)@MatrixVariable(矩阵变量)
表明方法参数应该被绑定到路径段中的一个 name-value 对
支持 @RequestMapping 的处理方法
如果方法参数类型是 java.util.Map,并且指定了一个矩阵变量名称,那么假设有适当的转换策略,矩阵变量值将被转换为 java.util.Map
如果方法参数是 Map<String, String> 或 MultiValueMap<String, String>,并且没有指定变量名称,那么 Map 将被填充所有的矩阵变量名称和值
(6)@CookieValue(获取 Cookie 值)
该注解表示一个方法参数被绑定到一个 HTTP cookie
方法参数可以被声明为 javax.servlet.http.cookie 类型或 cookie 值类型(String、int 等)
请注意,在 spring-webmvc 5.3.x 和更早的版本中,cookie 值是 URL 解码的。这将在 6.0 版本中改变,但与此同时,应用程序也可以声明 javax.servlet.http.Cookie 类型的参数来访问原始值
(7)@RequestBody(获取请求体)
该注解表明一个方法参数应该被绑定到 Web 请求体中
请求体通过一个 HttpMessageConverter 来解决方法参数,这取决于请求的内容类型
可选的是,通过用 @Valid 注释参数,可以应用自动验证
支持被注释的处理方法
(8)@ModelAttribute(获取 Model 属性)
将方法参数或方法返回值绑定到一个命名的模型属性的注解,暴露在 Web 视图中
支持带有 @RequestMapping 方法的控制器类
警告:数据绑定可能会导致安全问题,因为它暴露了对象图中不应该被外部客户端访问或修改的部分。因此,在设计和使用数据绑定时,应仔细考虑安全问题。更多细节,请参考参考手册中关于 Spring Web MVC 和 Spring WebFlux 的数据绑定的专门章节
通过注释 @RequestMapping 方法的相应参数,@ModelAttribute 可用于将命令对象暴露在 Web 视图中,使用特定的属性名称
@ModelAttribute 也可以通过用 @RequestMapping 方法注释控制器类中的访问器方法来向web视图暴露参考数据。这样的访问器方法被允许有任何 @RequestMapping 方法支持的参数,返回要公开的模型属性值
但是请注意,当请求处理导致异常时,参考数据和所有其他的模型内容对 Web 视图是不可用的,因为异常可能在任何时候被引发,使得模型的内容不可靠。出于这个原因,@ExceptionHandler 方法不提供对模型参数的访问
(9)示例
@RestController
public class ParameterTestController {
//car/2/owner/zhangsan?age=18&interest=a&interest=b&
@GetMapping("/car/{id}/owner/{username}")
public Map<String,Object> getCar(//获取路径变量
@PathVariable("id") Integer id,
@PathVariable("username") String name,
//以路径变量name作key,实参作value,封装到Map中
@PathVariable Map<String,String> pv,
//获取请求头指定参数
@RequestHeader("User-Agent") String userAgent,
//获取请求头所有参数
@RequestHeader Map<String,String> header,
//获取请求参数
@RequestParam("age") Integer age,
//List接收相同name请求参数的多个值
@RequestParam("interest") List<String> interest,
//以请求参数name作key,实参作value,所有请求参数封装到Map
@RequestParam Map<String,String> params,
//获取Cookie的_ga值,以值的类型String接收
@CookieValue("_ga") String _ga,
//获取Cookie的_ga值,以Cookie类型接收
@CookieValue("_ga") Cookie cookie) {
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
map.put("pv",pv);
map.put("userAgent",userAgent);
map.put("headers",header);
map.put("age",age);
map.put("interest",interest);
map.put("params",params);
map.put("_ga",_ga);
System.out.println(cookie.getName() + "->" + cookie.getValue());
return map;
}
//只有POST请求有请求体
@PostMapping("/save")
public Map postMethod(//获取请求体
@RequestBody String content){
Map<String,Object> map = new HashMap<>();
map.put("content",content);
return map;
}
/*
SpringBoot默认禁用矩阵变量功能,矩阵变量需要在SpringBoot中手动开启
原理:对于路径的处理,使用 UrlPathHelper 进行解析,其中属性removeSemicolonContent(移除分号内容)支持矩阵变量,默认为true
根据RFC3986的规范,矩阵变量应当绑定在路径变量中,矩阵变量必须有 url 路径变量才能被解析
若是有多个矩阵变量,使用英文符号 ; 进行分隔
若是一个矩阵变量有多个值,使用英文符号 , 进行分隔,或命名多个重复key即可
若不同路径变量有相同名称的值,使用pathVar区分
矩阵变量:与路径变量绑定,使用 ; 分隔
请求参数:以 ? 开始,使用 & 分隔
*/
//请求路径:/boss/A;age=20/B;age=10
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
Map<String,Object> map = new HashMap<>();
map.put("bossAge",bossAge);
map.put("empAge",empAge);
return map;
}
}
@Controller
public class RequestController {
@GetMapping("/forward")
public String forwardPage(HttpServletRequest request) {
request.setAttribute("message", "转发Request");
return "forward:/success";
}
@ResponseBody
@GetMapping("/success")
public Map success(//使用注解获取Request域特定属性
@RequestAttribute("message") String message1,
//转发原生Request
HttpServletRequest request) {
Object message2 = request.getAttribute("message");
Map<String, Object> map = new HashMap<>();
map.put("注解方式获取Request属性",message1);
map.put("原生方式获取Request属性",message2);
return map;
}
}
(10)手动开启矩阵变量功能
//方式一:实现WebMvcConfigurer接口
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
//设置为false,不移除分号后的内容,矩阵变量功能即生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
//方式二:往Spring容器直接注入WebMvcConfigurer
@Configuration(proxyBeanMethods = false)
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
//设置为false,不移除分号后的内容,矩阵变量功能即生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
}
}
2、Servlet API
(1)支持的参数类型(及其子类类型):WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
(2)ServletRequestMethodArgumentResolver (Servlet 请求方法参数解析器)
public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
(Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}
}
3、复杂参数
(1)Map、Model、Errors / BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(Servlet 中 Response 响应)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
(2)Map、Model 的数据会被放在请求域(HttpServletRequest), map.put、model.addAttribute 等价于 request.setAttribute
(3)参数解析器:Map:MapMethodProcesser;Model:ModelMethodProcesser
4、自定义对象参数
(1)可以自动类型转换与格式化,可以级联封装
(2)传入实参、返回值
(3)数据绑定:页面提交的请求数据(GET、POST)都可以与对象进行绑定
(4)参数解析器:ServletModelAttributeMethodProcessor,继承 ModelAttributeMethodProcessor
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (
//方法参数是否有ModelAttribute注解,或
parameter.hasParameterAnnotation(ModelAttribute.class) ||
//注解是否为必须的,且参数是否不为简单属性
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
String name = ModelFactory.getNameForParameter(parameter);
//获取参数注解
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
//判断mavContainer是否存在该自定义参数类型
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}
else {
//创建自定义类型的空实例
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
else {
attribute = ex.getTarget();
}
bindingResult = ex.getBindingResult();
}
}
//若绑定结果为null
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
/*
binderFactory(绑定工厂)创建WebDataBinder(Web数据绑定器):binder
binder将webRequest(原生Servlet请求参数的值)绑定到指定的JavaBean,即attribute(空对象)封装到binder的target属性中
*/
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
//binder 利用反射,与其中 Converters(转换器)将请求数据转成指定的数据类型,再次封装到JavaBean中
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
//若绑定期间出现异常,将绑定异常结果
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
}
public abstract class BeanUtils {
public static boolean isSimpleProperty(Class<?> type) {
Assert.notNull(type, "'type' must not be null");
return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type));
}
}
(5)GenericConversionService:在设置每一个值时,查找它其中所有 converter,可以将(request 参数的字符串)转换到指定类型(JavaBean 中的属性类型)
public class GenericConversionService implements ConfigurableConversionService {
@Nullable
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
//先从缓存中查找Converter
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
//缓存中没有匹配Converter,从静态内部类Converters中查找
converter = this.converters.find(sourceType, targetType);
if (converter == null) {
converter = getDefaultConverter(sourceType, targetType);
}
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
this.converterCache.put(key, NO_MATCH);
return null;
}
private static class Converters {
@Nullable
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Search the full type hierarchy
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
}
}
(6)允许 WebDataBinder 中存放自定义 Converter,解决无法绑定混合类型数据问题
//往Spring容器直接注入WebMvcConfigurer
@Configuration(proxyBeanMethods = false)
public class WebConfig {
//WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
//重写该方法,可以添加转换器和格式化器
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(
//添加转换器,第一个泛型为源类型,第二个泛型为目标类型
new Converter<String, Pet>() {
//重写转换方法
@Override
public Pet convert(String source) {
//猫,3
if(!StringUtils.isEmpty(source)){
Pet pet = new Pet();
String[] split = source.split(",");
pet.setName(split[0]);
pet.setAge(Integer.parseInt(split[1]));
return pet;
}
return null;
}
});
}
};
}
}
参数处理原理
1、DispatcherServlet 的 doDispatch 方法
(1)HandlerMapping 中找到能处理请求的 Handler
(2)为当前 Handler 找一个适配器 HandlerAdapter,如:RequestMappingHandlerAdapter 支持方法上标注 @RequestMapping、HandleFunctionrAdapter 支持函数式编程
(3)适配器执行目标方法并确定方法参数的每一个值
public class DispatcherServlet extends FrameworkServlet {
//传入原生HttpServletRequest、HttpServletResponse
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
//是否为文件上传请求,默认为false
boolean multipartRequestParsed = false;
//请求期间是否有异步,若存在,使用异步管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查是否为文件上传请求
processedRequest = checkMultipart(request);
//若为文件上传请求,则为true
multipartRequestParsed = (processedRequest != request);
/*
获取可以处理当前请求的Handler
mappedHandler:调用链,包含handler、interceptorList、interceptorIndex
handler:浏览器发送的请求所匹配的控制器方法
interceptorList:处理控制器方法的所有拦截器集合
interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//调用拦截器的preHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//调用拦截器的postHandle()
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//后续处理:处理模型数据和渲染视图
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()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
//遍历handlerAdapters
for (HandlerAdapter adapter : this.handlerAdapters) {
//判断当前Adapter是否适配Handler
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
}
2、获取适配当前 Handler 的 HandlerAdapter
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Override
public final boolean supports(Object handler) {
//Handler若为HandlerMethod类型
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
}
3、处理器适配器:HandlerAdapter
(1)获取 Handler
public class HandlerExecutionChain {
public Object getHandler() {
return this.handler;
}
}
(2)由 HandlerAdapter 执行 handler 方法
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//初始化过程
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//在invocableMethod(可执行方法)封装处理器,并设置argumentResolvers(参数解析器)、returnValueHandlers(返回值处理器)
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//参数解析器argumentResolvers不为null
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
//返回值处理器returnValueHandlers不为null
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//ServletInvocableHandlerMethod 的 invokeAndHandle 实际执行目标方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
}
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//invokeForRequest进入目标方法
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;
}
}
}
public class InvocableHandlerMethod extends HandlerMethod {
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//获取方法所有参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//利用反射,执行方法
return doInvoke(args);
}
//确定目标方法每一个参数的值
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取方法所有参数声明
MethodParameter[] parameters = getMethodParameters();
//若方法参数为空,则无需确定参数值
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
//创建Object数组,与参数数组等长
Object[] args = new Object[parameters.length];
//遍历参数数组
for (int i = 0; i < parameters.length; i++) {
//获取第i个参数
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
//判断参数不为null
if (args[i] != null) {
continue;
}
//判断参数解析器是否支持该参数类型
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//解析第i个参数,并传入arg[i]
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
@Nullable
protected Object doInvoke(Object... args) throws Exception {
Method method = getBridgedMethod();
try {
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
}
return method.invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(method, getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
}
(3)获取支持参数的参数解析器
(4)解析参数的值,调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//先从缓存中查看是否存在支持该参数的解析器,若存在,直接返回该参数解析器
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
//缓存中不存在支持该参数的解析器
if (result == null) {
//遍历所有参数解析器,依次判断是否支持传入参数
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
//把支持该参数的解析器,放入缓存,提高下次调用该参数解析器的运行速度
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
}
4、参数解析器接口:HandlerMethodArgumentResolver
(1)确定将要执行的目标方法的每一个参数的值是什么
(2)参数解析器决定 SpringMVC 目标方法的参数类型的种类个数
(3)执行流程:当前解析器调用 supportsParameter ,判断是否支持解析该参数 -> 若支持,则当前解析器调用 resolveArgument
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
5、目标方法执行完成:将所有的数据存放在 ModelAndViewContainer,其中包含跳转的页面地址 View,和 Model 数据
6、处理派发结果
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception
protected abstract void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
HttpServletRequest request) throws Exception {
//遍历model,所有数据存放到请求域
model.forEach((name, value) -> {
if (value != null) {
request.setAttribute(name, value);
}
else {
request.removeAttribute(name);
}
});
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战