SpringMVC中重定向请求时传输参数原理分析与实践

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: SpringMVC中重定向请求时传输参数原理分析与实践

本文要解决的是重定向请求时,把数据传到目标地方。

【1】几个概念

① Redirect Attributes

默认情况下,所有模型属性都被视为在重定向URL中作为URI模板变量暴露。在其余的属性中,基本类型或基本类型的集合或基本类型的数组将自动附加为查询参数。


如果专门为重定向准备了一个模型实例,那么将基本类型属性作为查询参数附加可能是理想的结果。但是,在带注解的控制器中,模型可以包含为渲染目的添加的其他属性(例如,下拉字段值)。为了避免此类属性出现在URL中,@RequestMapping方法可以声明类型为RedirectAttributes 的参数,并使用它指定可供RedirectView使用的确切属性。如果方法确实重定向,则使用RedirectAttributes 的内容。否则,将使用model的内容。


RequestMappingHandlerAdapter 提供了一个标记叫做ignoreDefaultModelOnRedirect,可以用来声明如果控制器方法重定向时,默认的Model是否使用。RequestMappingHandlerAdapter提供了一个名为ignoreDefaultModelOnRedirect的标志,您可以使用该标志指示如果控制器方法重定向,则不应使用默认模型的内容。相反,控制器方法应该声明RedirectAttributes类型的属性,如果不这样做,则不应该将任何属性传递给RedirectView。MVC命名空间和MVC Java配置都将此标志设置为false,以保持向后兼容性。但是,对于新应用程序,我们建议将其设置为true。

请注意,当前请求中的URI模板变量在扩展重定向URL时自动可用,您不需要通过模型或重定向属性显式添加它们。以下示例显示如何定义重定向:

@PostMapping("/files/{path}")
public String upload(...) {
    // ...
    return "redirect:files/{path}";
}

向重定向目标传递数据的另一种方法是使用flash属性。与其他重定向属性不同,flash属性保存在HTTP会话中(因此不会出现在URL中)。

② Flash Attributes

Flash属性为一个请求提供了一种方法来存储打算在另一个请求中使用的属性。这是重定向时最常用的 — 例如Post-Redirect-Get模式。flash属性在重定向之前(通常在会话中)临时保存,以便在重定向后可用于请求,之后会被删除。

SpringMVC有两个主要的类来支持flash属性。FlashMap类用于保存flash属性,而FlashMapManager接口的实例用于存储、检索和管理FlashMap实例。

默认支持Flash 属性,你不需要显示进行启用。但是如果不使用,它不会导致HTTP会话创建。在每个请求上,都有一个 “input” FlashMap和一个“output” FlashMap。前者具有从上一个请求(如果有)传递的属性,后者具有为后续请求保存的属性。这两个FlashMap实例都可以通过RequestContextUtils中的静态方法从SpringMVC中的任何位置访问。

public static Map<String, ?> getInputFlashMap(HttpServletRequest request) {
  return (Map<String, ?>) request.getAttribute(DispatcherServlet.INPUT_FLASH_MAP_ATTRIBUTE);
}
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
public static FlashMap getOutputFlashMap(HttpServletRequest request) {
  return (FlashMap) request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE);
}
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";

带注解的控制器通常不需要直接使用FlashMap。相反,@RequestMapping注解的方法可以接受RedirectAttributes 类型的参数,并使用它为重定向场景添加flash属性。通过RedirectAttributes 添加的Flash属性会自动传播到“output”FlashMap。类似地,在重定向之后,“input”FlashMap中的属性会自动添加到为目标URL提供服务的控制器的Model中。

为flash 属性匹配请求

flash属性的概念存在于许多其他web框架中,并且已经证明有时会遇到并发问题。这是因为,根据定义,flash属性将存储到下一个请求。但是,“下一个”请求可能不是预期的接收者,而是另一个异步请求(例如,轮询或资源请求),在这种情况下,flash属性被过早删除。


为了减少出现此类问题的可能性,RedirectView 会使用目标重定向URL的路径和查询参数自动“标记”FlashMap实例。反过来,默认FlashMapManager在查找“input”FlashMap时将该信息与传入请求相匹配。

// DispatcherServlet#doService有如下代码来获取inputFlashMap 
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
//具体获取inputFlashMap 方法如下
@Override
@Nullable
public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {
//从session中获取FLASH_MAPS_SESSION_ATTRIBUTE
  List<FlashMap> allFlashMaps = retrieveFlashMaps(request);
  if (CollectionUtils.isEmpty(allFlashMaps)) {
    return null;
  }
// 获取过期的FlashMap
  List<FlashMap> mapsToRemove = getExpiredFlashMaps(allFlashMaps);
  //获取与当前请求匹配的FlashMap 
  FlashMap match = getMatchingFlashMap(allFlashMaps, request);
  if (match != null) {
    mapsToRemove.add(match);
  }
// 从allFlashMaps移除掉mapsToRemove,然后更新session的FLASH_MAPS_SESSION_ATTRIBUTE
  if (!mapsToRemove.isEmpty()) {
    Object mutex = getFlashMapsMutex(request);
    if (mutex != null) {
      synchronized (mutex) {
        allFlashMaps = retrieveFlashMaps(request);
        if (allFlashMaps != null) {
          allFlashMaps.removeAll(mapsToRemove);
          updateFlashMaps(allFlashMaps, request, response);
        }
      }
    }
    else {
      allFlashMaps.removeAll(mapsToRemove);
      updateFlashMaps(allFlashMaps, request, response);
    }
  }
// 返回当前请求匹配的FlashMap
  return match;
}

这并不能完全消除并发问题的可能性,但可以通过重定向URL中已有的信息大大减少并发问题。因此,我们建议您主要在重定向场景中使用flash属性。

【2】实践实例

常见的可以使用URL传参、uriPathVariable,如/testr?name=jane/testr/{jane}。除此之外,可以考虑使用flash attributes。

一个请求在实际转发到doDispatch前,请求属性里面会有如下属性:

org.springframework.web.context.request.async.WebAsyncManager.WEB_ASYNC_MANAGER--WebAsyncManager
CharacterEncodingFilter.FILTERED--true
org.springframework.web.servlet.DispatcherServlet.CONTEXT--WebApplicationContext for namespace 'DispatcherServlet-servlet', started on Fri Dec 10 15:45:06 CST 2021
org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER--AcceptHeaderLocaleResolver
org.springframework.web.servlet.DispatcherServlet.OUTPUT_FLASH_MAP--FlashMap
HiddenHttpMethodFilter.FILTERED--true
org.springframework.web.servlet.DispatcherServlet.FLASH_MAP_MANAGER--SessionFlashMapManager
org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER--FixedThemeResolver
org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE--WebApplicationContext for namespace 'DispatcherServlet-servlet', started on Fri Dec 10 15:45:06 CST 2021

可以看到此时有一个空的outputFlashMap,但是没有inputFlashMap。

① redirectAttributes发送,model接收

如下所示,不通过URL传参,testRedirect请求重定向到testr。

// 重定向前的方法
@RequestMapping("/testRedirect")
public String testRedirect(HttpServletRequest request, RedirectAttributes redirectAttributes){
// 普通属性
    redirectAttributes.addAttribute("redirectAttributes","redirectAttributesValue");
//flash 属性
    redirectAttributes.addFlashAttribute("addFlashAttribute","redirectAttributes.addFlashAttribute-value");
    return "redirect:/testr";
}
// 重定向后的
@RequestMapping("/testr")
public String testr(HttpServletRequest request, String redirectAttributes, String addFlashAttribute,Model model){
// redirectAttributesValue
    System.out.println(request.getParameter("redirectAttributes"));
// null
    System.out.println(request.getParameter("addFlashAttribute"));
// {addFlashAttribute=redirectAttributes.addFlashAttribute-value}
    System.out.println(model);
    return "success";
}

重定向URL这里最终为:

http://localhost:8080/testr?redirectAttributes=redirectAttributesValue

可以看到方法testRedirect中redirectAttributes.addAttribute("redirectAttributes","redirectAttributesValue");被作为URL的queryString参数。此时方法testr中request中获取不到FlashAttribute,model里面可以获取到FlashAttribute

② 原理分析

// 路径链如下
DispatcherServlet#doService--
DispatcherServlet#doDispatch--
AbstractHandlerMethodAdapter#handle--
RequestMappingHandlerAdapter#handleInternal--
RequestMappingHandlerAdapter#invokeHandlerMethod--
ServletInvocableHandlerMethod#invokeAndHandle--
RequestMappingHandlerAdapter#getModelAndView

① testRedirect目标方法处理前,DispatcherServlet#doService中,此时inputFlashMap为null,outputFlashMap为new FlashMap()。

默认情况下,这时是没有inputFlashMap的。除非已经有其他重定向请求使用了flashMap且没有过期并匹配当前请求。

② 获取mavContainer实例后放入inputFlashMap

RequestMappingHandlerAdapter#invokeHandlerMethod方法中,获取到ModelAndViewContainer实例后会从request中获取属性DispatcherServlet.INPUT_FLASH_MAP_ATTRIBUTE也就是获取inputFlashMap放入到model中。这里面默认放入的是defaultModel而非redirectModel(默认情况下redirectModelScenario是false,ignoreDefaultModelOnRedirect也是false。)


另外,这里默认为mavContainer设置ignoreDefaultModelOnRedirect=false。

mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

③ testRedirect目标方法处理

我们先看一下方法String testRedirect(HttpServletRequest request, RedirectAttributes redirectAttributes)解析参数结果(RedirectAttributes实例是一个RedirectAttributesModelMap,该参数被RedirectAttributesMethodArgumentResolver解析):

RedirectAttributesModelMap 主要属性如下所示,也就是说比default model多了一个private final ModelMap flashAttributes = new ModelMap();

public class RedirectAttributesModelMap extends ModelMap implements RedirectAttributes {
  // 数据绑定器
  @Nullable
  private final DataBinder dataBinder;
  // 存放flashAttribute
  private final ModelMap flashAttributes = new ModelMap();
}

RedirectAttributesMethodArgumentResolver解析RedirectAttributes参数的过程如下所示:

@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  Assert.state(mavContainer != null, "RedirectAttributes argument only supported on regular handler methods");
  ModelMap redirectAttributes;
  // 实例化RedirectAttributesModelMap
  if (binderFactory != null) {
    DataBinder dataBinder = binderFactory.createBinder(webRequest, null, DataBinder.DEFAULT_OBJECT_NAME);
    redirectAttributes = new RedirectAttributesModelMap(dataBinder);
  }
  else {
    redirectAttributes  = new RedirectAttributesModelMap();
  }
  // 将当前redirectAttributes赋予mavContainer的RedirectModel属性
  mavContainer.setRedirectModel(redirectAttributes);
  return redirectAttributes;
}

这里非常有必要说的是,如果目前方法有参数redirectAttributes ,那么在解析目标方法参数后,mavContainer中的RedirectModel就是redirectAttributes 实例对象!


然后目标方法则会分别向默认集合与flashAttributes中放入数据:

// 普通属性
redirectAttributes.addAttribute("redirectAttributes","redirectAttributesValue");
//flash 属性
redirectAttributes.addFlashAttribute("addFlashAttribute","redirectAttributes.addFlashAttribute-value");

④ testRedirect方法返回结果处理

返回结果是redirect:/testr,这里使用ViewNameMethodReturnValueHandlr处理返回结果。过程如下所示,这里会将mavContainer的RedirectModelScenario属性设置为true。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  if (returnValue instanceof CharSequence) {
    String viewName = returnValue.toString();
    mavContainer.setViewName(viewName);
    if (isRedirectViewName(viewName)) {
    // 这里设置RedirectModelScenario为true哦
      mavContainer.setRedirectModelScenario(true);
    }
  }
  else if (returnValue != null) {
    // should not happen
    throw new UnsupportedOperationException("Unexpected return type: " +
        returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
  }
}

⑤ 获取ModelAndView

如下所示,在RequestMappingHandlerAdapter#getModelAndView方法中(此时已经调用目标方法并对返回结果做了处理),判断model是RedirectAttributes,则会读取flashAttributes 然后放入OutputFlashMap中。

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
    ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
  modelFactory.updateModel(webRequest, mavContainer);
  if (mavContainer.isRequestHandled()) {
    return null;
  }
  ModelMap model = mavContainer.getModel();
  ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
  if (!mavContainer.isViewReference()) {
    mav.setView((View) mavContainer.getView());
  }
  if (model instanceof RedirectAttributes) {
    Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
    if (request != null) {
// request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE);
      RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
    }
  }
  return mav;
}

如下过程所示,这里会将RedirectAttributes的flash属性放入到outputFlashMap中,但是RedirectAttributes中还有普通属性没有处理哦。


⑥ 渲染视图

RedirectView#renderMergedOutputModel方法是AbstractView#render方法的核心方法。其会获取目标URL(会将model中的属性-值作为URL的queryString Param拼接到URL后面),也就是这里会处理RedirectAttributes的普通属性。

@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
    HttpServletResponse response) throws IOException {
// 这里很关键,会将RedirectAttributes中的普通属性拼接作为url 参数
  String targetUrl = createTargetUrl(model, request);
  targetUrl = updateTargetUrl(targetUrl, model, request, response);
  // Save flash attributes
  RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);
  // Redirect
  sendRedirect(request, response, targetUrl, this.http10Compatible);
}

解析targetUrl 过程如下所示:

我们再继续看一下RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);过程:

public static void saveOutputFlashMap(String location, HttpServletRequest request, HttpServletResponse response) {
  FlashMap flashMap = getOutputFlashMap(request);
  if (CollectionUtils.isEmpty(flashMap)) {
    return;
  }
  UriComponents uriComponents = UriComponentsBuilder.fromUriString(location).build();
  flashMap.setTargetRequestPath(uriComponents.getPath());
  flashMap.addTargetRequestParams(uriComponents.getQueryParams());
  FlashMapManager manager = getFlashMapManager(request);
  Assert.state(manager != null, "No FlashMapManager. Is this a DispatcherServlet handled request?");
  // 这里又会调用FlashMapManager 的saveOutputFlashMap方法
  manager.saveOutputFlashMap(flashMap, request, response);
}

FlashMapManager 的saveOutputFlashMap方法如下所示,这里很重要的一步是会获取sessionFlashMap-List allFlashMaps(FLASH_MAPS_SESSION_ATTRIBUTE),然后将outputFlashMap保存更新进去。给重定向后的请求使用。

@Override
public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
  if (CollectionUtils.isEmpty(flashMap)) {
    return;
  }
  String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);
  // 设置目标请求地址
  flashMap.setTargetRequestPath(path);
// 设置过期时间,默认是180
  flashMap.startExpirationPeriod(getFlashMapTimeout());
  Object mutex = getFlashMapsMutex(request);
  if (mutex != null) {
    synchronized (mutex) {
    // 从session中获取属性FLASH_MAPS_SESSION_ATTRIBUTE对应的值
      List<FlashMap> allFlashMaps = retrieveFlashMaps(request);
      allFlashMaps = (allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList<>());
      // 放入flashMap
      allFlashMaps.add(flashMap);
      // 将FLASH_MAPS_SESSION_ATTRIBUTE -- allFlashMaps  放入session中
      updateFlashMaps(allFlashMaps, request, response);
    }
  }
  else {
    List<FlashMap> allFlashMaps = retrieveFlashMaps(request);
    allFlashMaps = (allFlashMaps != null ? allFlashMaps : new ArrayList<>(1));
    allFlashMaps.add(flashMap);
    updateFlashMaps(allFlashMaps, request, response);
  }
}

详细过程如下图所示

⑦ 新请求从session中获取inputFlashMap

核心代码如下,从session中获取到inputFlashMap(然后会从session移除掉哦)。如果inputFlashMap不为null,则放入request属性中。然后放入OUTPUT_FLASH_MAP_ATTRIBUTE和FLASH_MAP_MANAGER_ATTRIBUTE

if (this.flashMapManager != null) {
  FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  // 如果inputFlashMap  不为null,则放入request中一个不可修改的inputFlashMap
  if (inputFlashMap != null) {
    request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
  }
  // 放入outputFlashMap
  request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  // 放入flashMapManager
  request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}

这里面我们看一下retrieveAndUpdate方法。这个方法很有意思,首先从session中获取FLASH_MAPS_SESSION_ATTRIBUTE属性对应的值对象我们姑且称之为sessionFlashMap。然后获取sessionFlashMap中过期的ExpiredFlashMaps以及与当前请求匹配的MatchingFlashMap。


接下来要做的就是从sessionFlashMap移除掉ExpiredFlashMaps和MatchingFlashMap,然后更新session的FLASH_MAPS_SESSION_ATTRIBUTE属性值,并将MatchingFlashMap作为方法结果返回。

@Override
@Nullable
public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {
// 获取session中的flashMap
  List<FlashMap> allFlashMaps = retrieveFlashMaps(request);
  if (CollectionUtils.isEmpty(allFlashMaps)) {
    return null;
  }
// 获取过期的flashMap
  List<FlashMap> mapsToRemove = getExpiredFlashMaps(allFlashMaps);
  // 获取匹配当前请求的flashMap
  FlashMap match = getMatchingFlashMap(allFlashMaps, request);
  if (match != null) {
    mapsToRemove.add(match);
  }
// 移除掉mapsToRemove,然后将剩余的更新session中的flashMap
  if (!mapsToRemove.isEmpty()) {
    Object mutex = getFlashMapsMutex(request);
    if (mutex != null) {
      synchronized (mutex) {
        allFlashMaps = retrieveFlashMaps(request);
        if (allFlashMaps != null) {
          allFlashMaps.removeAll(mapsToRemove);
          updateFlashMaps(allFlashMaps, request, response);
        }
      }
    }
    else {
      allFlashMaps.removeAll(mapsToRemove);
      updateFlashMaps(allFlashMaps, request, response);
    }
  }
// 返回匹配的flashMap
  return match;
}

⑧ 新请求的mavContainer放入inputFlashMap

RequestMappingHandlerAdapter#invokeHandlerMethod方法中有如下代码会从request中获取InputFlashMap然后放入mavContainerdefaultModel中。

mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));

那么此时再解析重定向后的方法参数,很显然将会得到如下结果:

至此,我们讲重定向前后参数传参分析完毕。

③ Model可以实现重定向传参的需求吗?

这里的model默认是指BindingAwareModelMap,其不能实现RedirectAttributesModelMap的功能。


如下所示,方法重定向前在model中放入modelAttr-modelAttrValue,但是方法重定向后,是接收不到该键值对的。

如下所示,方法重定向后的model中只有addFlashAttribute属性:


{addFlashAttribute=redirectAttributes.addFlashAttribute-value}

因为在处理返回结果时,已经将redirectModelScenario设置为true并且redirectModel不为null,那么在getModelAndView方法中获取Model时将会获取到redirectModel也就是RedirectAttributesModelMap实例。该实例的flashAttributes数据将会放入outputFlashMap中,而该实例非flashAttributes数据将会用来构造ModelAndView实例。

ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
// 获取model的代码如下
public ModelMap getModel() {
  if (useDefaultModel()) {
    return this.defaultModel;
  }
  else {
    if (this.redirectModel == null) {
      this.redirectModel = new ModelMap();
    }
    return this.redirectModel;
  }
}

为啥redirectAttributes.addAttribute("redirectAttributes","redirectAttributesValue");同样能把属性带过去呢?因为其将作为URL的query String Params拼接到URL后面带过去。


如下所示RedirectView的属性中exposeModelAttributes 、expandUriTemplateVariables 默认为true。

private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
private boolean contextRelative = false;
private boolean http10Compatible = true;
private boolean exposeModelAttributes = true;
@Nullable
private String encodingScheme;
@Nullable
private HttpStatus statusCode;
private boolean expandUriTemplateVariables = true;
private boolean propagateQueryParams = false;

这两个属性有什么作用呢?我们可以细探究一下RedirectView解析目标URL的过程

protected final String createTargetUrl(Map<String, Object> model, HttpServletRequest request)
    throws UnsupportedEncodingException {
  // Prepare target URL.
  StringBuilder targetUrl = new StringBuilder();
  // 获取请求url
  String url = getUrl();
  Assert.state(url != null, "'url' not set");
// 拼接contextPath
  if (this.contextRelative && getUrl().startsWith("/")) {
    // Do not apply context path to relative URLs.
    targetUrl.append(getContextPath(request));
  }
  targetUrl.append(getUrl());
  String enc = this.encodingScheme;
  if (enc == null) {
    enc = request.getCharacterEncoding();
  }
  if (enc == null) {
    enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
  }
// 尝试用model的数据对url上的占位符进行解析替代
  if (this.expandUriTemplateVariables && StringUtils.hasText(targetUrl)) {
    Map<String, String> variables = getCurrentRequestUriVariables(request);
    targetUrl = replaceUriTemplateVariables(targetUrl.toString(), model, variables, enc);
  }
  // 是否将当前请求参数拼接到URL后面,propagateQueryParams默认false
  if (isPropagateQueryProperties()) {
    appendCurrentQueryParams(targetUrl, request);
  }
  // 是否将model里面属性暴露出来--拼接到URL后面
  if (this.exposeModelAttributes) {
    appendQueryProperties(targetUrl, model, enc);
  }
// /testr?redirectAttributes=redirectAttributesValue
  return targetUrl.toString();
}


目录
相关文章
|
23天前
|
设计模式 前端开发 Java
步步深入SpringMvc DispatcherServlet源码掌握springmvc全流程原理
通过对 `DispatcherServlet`源码的深入剖析,我们了解了SpringMVC请求处理的全流程。`DispatcherServlet`作为前端控制器,负责请求的接收和分发,处理器映射和适配负责将请求分派到具体的处理器方法,视图解析器负责生成和渲染视图。理解这些核心组件及其交互原理,有助于开发者更好地使用和扩展SpringMVC框架。
38 4
|
2月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
154 2
|
2月前
|
前端开发 Java UED
SpringMVC全局异常处理+拦截器使用+参数校验
通过使用 SpringMVC 的全局异常处理、拦截器和参数校验,可以有效提升 Web 应用程序的安全性、稳定性和用户体验。这些技术的合理应用,不仅可以保证代码的健壮性,还能提高代码的可维护性,为开发高质量的 Web 应用程序提供了坚实的基础。
61 6
|
3月前
|
设计模式 前端开发 Java
Spring MVC——项目创建和建立请求连接
MVC是一种软件架构设计模式,将应用分为模型、视图和控制器三部分。Spring MVC是基于MVC模式的Web框架,通过`@RequestMapping`等注解实现URL路由映射,支持GET和POST请求,并可传递参数。创建Spring MVC项目与Spring Boot类似,使用`@RestController`注解标记控制器类。
53 1
Spring MVC——项目创建和建立请求连接
|
3月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
4月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
|
3月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
68 2
|
3月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
241 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
3月前
|
前端开发 Java
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
文章介绍了如何使用SpringBoot创建简单的后端服务器来处理HTTP请求,包括建立连接、编写Controller处理请求,并返回响应给前端或网址。
64 0
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
|
4月前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
下一篇
开通oss服务