SpringBoot2核心技术(核心功能)- 05、Web开发【5.3 请求参数处理】

news2024/11/18 4:39:55

5.3、请求参数处理

0、请求映射

1、rest使用与原理

  • @xxxMapping;
  • Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
    • 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
    • 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户【即所有对用户的操作都在/user完成,仅靠请求方式来区分】
    • 为了能够采取现在的方法需要引入核心Filter;HiddenHttpMethodFilter
      • 用法: 表单method=post,隐藏域 _method=put
      • SpringBoot中手动开启
    • 扩展:如何把_method 这个名字换成我们自己喜欢的。

REST风格测试:
在这里插入图片描述
在这里插入图片描述
运行结果:
在这里插入图片描述
在这里插入图片描述
注意:PUT和DELETE访问得到的都是GET-张三
原因:put和delete这两个method并不支持,要想生效,需要为其进行配置
HiddenHttpMethodFilter

<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT"/>
    <input value="REST-PUT 提交" type="submit"/>

再次测试
在这里插入图片描述
发现put和delete访问得到POST-张三
原因:相关功能spring.mvc.hiddenmethod.filter默认不开启,需要在yaml里设置开启
在这里插入图片描述
在这里插入图片描述
可以用了

Rest原理(表单提交要使用REST的时候)

  • 表单提交会带上_method=PUT
  • 请求过来会被HiddenHttpMethodFilter拦截
    • 首先判断请求是否正常,并且是POST
      • 然后获取到_method的值【会自动转换为大写,所以写成delete也行】。
      • 判断_method是否为支持请求方式;默认兼容以下请求;PUT/DELETE/PATCH
      • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。【装饰器模式】
      • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。

但是: Rest使用客户端工具

  • 如PostMan直接发送put、delete等方式请求,就不需要Filter,直接过关。
    在这里插入图片描述
    此外:注解@RequestMapping(value=Y,method=XX)
    可以直接用@XXMapping(Y)代替
    在这里插入图片描述
    扩展:如何把_method 这个名字换成我们自己喜欢的。
    重新放一个HiddenHttpMethodFilter组件到容器中setMethodParam
    在这里插入图片描述
    在这里插入图片描述

完整代码如下:

======HelloController===========
 @RestController
public class HelloController {
   @RequestMapping("/1.jpg")
   public String hello(){
       return "aaaa";
   }

//    @RequestMapping(value = "/user",method = RequestMethod.GET)
   @GetMapping("/user")
   public String getUser(){
       return "GET-张三";
   }
//    @RequestMapping(value = "/user",method = RequestMethod.POST)
   @PostMapping("/user")
   public String saveUser(){
       return "POST-张三";
   }

//    @RequestMapping(value = "/user",method = RequestMethod.PUT)
   @PutMapping("/user")
   public String putUser(){
       return "PUT-张三";
   }
//    @RequestMapping(value = "/user",method = RequestMethod.DELETE)
   @DeleteMapping("/user")
   public String deleteUser(){
       return "DELETE-张三";
   }


}

============WebMvcAutoConfiguration=====
=====这个是解释配置的,不是需要写的代码=====

   @Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
   @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
   public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
   	return new OrderedHiddenHttpMethodFilter();
   }


//自定义filter
   @Bean
   public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
       HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
       methodFilter.setMethodParam("_m");
       return methodFilter;
   }
=======index.html=====
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>ZJ,欢迎您</h1>
测试REST风格;
<form action="/user" method="get">
    <input value="REST-GET 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input value="REST-POST 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="delete"/>
    <input name="_m" type="hidden" value="delete"/>
    <input value="REST-DELETE 提交" type="submit"/>
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT"/>
    <input value="REST-PUT 提交" type="submit"/>
</form>
<hr/>
测试基本注解:
<ul>
    <a href="car/3/owner/lisi?age=18&inters=basketball&inters=game">car/{id}/owner/{username}</a>
    <li>@PathVariable(路径变量)</li>
    <li>@RequestHeader(获取请求头)</li>
    <li>@RequestParam(获取请求参数)</li>
    <li>@CookieValue(获取cookie值)</li>
    <li>@RequestBody(获取请求体[POST])</li>

    <li>@RequestAttribute(获取request域属性)</li>
    <li>@MatrixVariable(矩阵变量)</li>
</ul>

/cars/{path}?xxx=xxx&aaa=ccc queryString 查询字符串。@RequestParam;<br/>
/cars/sell;low=34;brand=byd,audi,yd  ;矩阵变量 <br/>
页面开发,cookie禁用了,session里面的内容怎么使用;
session.set(a,b)---> jsessionid ---> cookie ----> 每次发请求携带。
url重写:/abc;jsesssionid=xxxx 把cookie的值使用矩阵变量的方式进行传递.

/boss/1/2

/boss/1;age=20/2;age=20

<a href="/cars/sell;low=34;brand=byd,audi,yd">@MatrixVariable(矩阵变量)</a>
<a href="/cars/sell;low=34;brand=byd;brand=audi;brand=yd">@MatrixVariable(矩阵变量)</a>
<a href="/boss/1;age=20/2;age=10">@MatrixVariable(矩阵变量)/boss/{bossId}/{empId}</a>
<br/>
<form action="/save" method="post">
    测试@RequestBody获取数据 <br/>
    用户名:<input name="userName"/> <br>
    邮箱:<input name="email"/>
    <input type="submit" value="提交"/>
</form>
<ol>
    <li>矩阵变量需要在SpringBoot中手动开启</li>
    <li>根据RFC3986的规范,矩阵变量应当绑定在路径变量中!</li>
    <li>若是有多个矩阵变量,应当使用英文符号;进行分隔。</li>
    <li>若是一个矩阵变量有多个值,应当使用英文符号,进行分隔,或之命名多个重复的key即可。</li>
    <li>如:/cars/sell;low=34;brand=byd,audi,yd</li>
</ol>
<hr/>
测试原生API:
<a href="/testapi">测试原生API</a>
<hr/>
测试复杂类型:<hr/>
测试封装POJO;
<form action="/saveuser" method="post">
    姓名: <input name="userName" value="zhangsan"/> <br/>
    年龄: <input name="age" value="18"/> <br/>
    生日: <input name="birth" value="2019/12/10"/> <br/>
    <!--    宠物姓名:<input name="pet.name" value="阿猫"/><br/>-->
    <!--    宠物年龄:<input name="pet.age" value="5"/>-->
    宠物: <input name="pet" value="啊猫,3"/>
    <input type="submit" value="保存"/>
</form>

<br>
</body>
</html>
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true   #开启页面表单的Rest功能,可选择性开启,默认false

2、请求映射原理

在这里插入图片描述
在这里插入图片描述
SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet的 **doDispatch()**方法开始,这是处理所有请求的方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 找到当前请求使用哪个Handler(Controller的方法)处理
				mappedHandler = getHandler(processedRequest);
                
                //HandlerMapping:处理器映射。/xxx->>xxxx

在这里插入图片描述

RequestMappingHandlerMapping: 保存了所有@RequestMapping 和handler的映射规则。
在这里插入图片描述
2.7.6的在这
在这里插入图片描述
在这里插入图片描述

所有的请求映射都在HandlerMapping中。

  • SpringBoot自动配置了欢迎页的 WelcomePageHandlerMapping 。访问 / 能访问到index.html;
  • SpringBoot自动配置了默认的 RequestMappingHandlerMapping
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping
  • 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
	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;
	}

1、普通参数与基本注解

1.1、注解:

@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
在这里插入图片描述

@RestController
public class ParameterTestController {


 //  car/2/owner/zhangsan
 //路径变量:get请求直接获取url路径信息
    @GetMapping("/car/{id}/owner/{username}")
    public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                     @PathVariable("username") String name,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     @RequestParam("inters") List<String> inters,
                                     @RequestParam Map<String,String> params,
                          
//这里_ga取决于自己的电脑,我的是Pycharm-50f35345
                                                            @CookieValue("_ga") String _ga,
                                     @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("inters",inters);
        map.put("params",params);
        map.put("_ga",_ga);
        System.out.println(cookie.getName()+"===>"+cookie.getValue());
        return map;
    }


    @PostMapping("/save")
    public Map postMethod(@RequestBody String content){
        Map<String,Object> map = new HashMap<>();
        map.put("content",content);
        return map;
    }


    //1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
    //2、SpringBoot默认是禁用了矩阵变量的功能
    //      手动开启:原理。对于路径的处理。UrlPathHelper进行解析。
    //              removeSemicolonContent(移除分号内容)支持矩阵变量的
    //3、矩阵变量必须有url路径变量才能被解析
    @GetMapping("/cars/{path}")
    public Map carsSell(@MatrixVariable("low") Integer low,
                        @MatrixVariable("brand") List<String> brand,
                        @PathVariable("path") String path){
        Map<String,Object> map = new HashMap<>();

        map.put("low",low);
        map.put("brand",brand);
        map.put("path",path);
        return map;
    }

    // /boss/1;age=20/2;age=10
    //如果矩阵变量遇到了相同的变量名可以通过制定路径来消除歧义【PathVar】,如上boss的age以及emp的age
    
    
    @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;

    }

}

PS1:
@Restcontroller和@Controller区别是什么?

  • 1.功能不同: 用@Controller配合视图解析器才能返回到指定页面。在对应的方法上加上@ResponseBody注解才能返回JSON,XML或自定义mediaType的内容到页面。
  • 2.用法不同: 不可以只用@RestController注解Controller,因为这样会让Controller中的内容不能返回jsp页面,而且会直接返回Return里的内容。
  • 3.作用不同:@RestController相当于@Controller和@ResponseBody两者合并起来的作用。

@MatrixVariable
开启矩阵变量功能,需要在WebConfig进行自定义配置,代码如下

@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_m");
        return methodFilter;
    }

    //开启矩阵变量功能的两种写法
    //1.在配置类中重写configurePathMatch方法
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }
    //2.在配置类中注入一个UrlPathHelper对象
//    @Override
//    public void configurePathMatch(PathMatchConfigurer configurer){
//        UrlPathHelper urlPathHelper = new UrlPathHelper();
//        //不移除;后面的内容。矩阵变量功能就可以生效
//        urlPathHelper.setRemoveSemicolonContent(false);
//        configurer.setUrlPathHelper(urlPathHelper);
//    }
}

1.2、Servlet API:

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId

ServletRequestMethodArgumentResolver 能够解析 以上的部分参数

@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) ||
				InputStream.class.isAssignableFrom(paramType) ||
				Reader.class.isAssignableFrom(paramType) ||
				HttpMethod.class == paramType ||
				Locale.class == paramType ||
				TimeZone.class == paramType ||
				ZoneId.class == paramType);
	}

1.3、复杂参数:

Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

Map<String,Object> map,  Model model, HttpServletRequest request 都是可以给request域中放数据,
request.getAttribute();
MapModel类型的参数,会返回 mavContainer.getModel();---> BindingAwareModelMapModel 也是Map
mavContainer.getModel(); 获取到值的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4、自定义对象参数:

可以自动类型转换与格式化,可以级联封装。

/**
 *     姓名: <input name="userName"/> <br/>
 *     年龄: <input name="age"/> <br/>
 *     生日: <input name="birth"/> <br/>
 *     宠物姓名:<input name="pet.name"/><br/>
 *     宠物年龄:<input name="pet.age"/>
 */
@Data
public class Person {
    
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
    
}

@Data
public class Pet {

    private String name;
    private String age;

}

result

2、POJO封装过程

● ServletModelAttributeMethodProcessor

3、参数处理原理

● HandlerMapping中找到能处理请求的Handler(Controller.method())
● 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
● 适配器执行目标方法并确定方法参数的每一个值

1、HandlerAdapter

在这里插入图片描述

0 - 支持方法上标注@RequestMapping
1 - 支持函数式编程的
xxxxxx

2、执行目标方法

// Actually invoke the handler.
//DispatcherServlet -- doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 //执行目标方法:先进行参数解析,然后才调用真正的处理方法 
mav = invokeHandlerMethod(request, response, handlerMethod);

//真正执行目标方法,包括确定每一个参数里的值
//ServletInvocableHandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

3、参数解析器-HandlerMethodArgumentResolver

确定将要执行的目标方法的每一个参数的值是什么;
SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
在这里插入图片描述
在这里插入图片描述
● SupportsParameter判断当前解析器是否支持解析这种参数
● 支持就调用 resolveArgument

4、返回值处理器

在这里插入图片描述

5、如何确定目标方法每一个参数的值

============InvocableHandlerMethod==========================
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				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;
	}

5.1、挨个判断所有参数解析器那个支持解析这个参数

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

5.2、解析这个参数的值

调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可

5.3、自定义类型参数 封装POJO

ServletModelAttributeMethodProcessor 这个参数处理器支持
是否为简单类型。

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

		if (mavContainer.containsAttribute(name)) {
			attribute = mavContainer.getModel().get(name);
		}
		else {
			// Create attribute instance
			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();
				}
				bindingResult = ex.getBindingResult();
			}
		}

		if (bindingResult == null) {
			// Bean property binding and validation;
			// skipped in case of binding failure on construction.
			WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
			if (binder.getTarget() != null) {
				if (!mavContainer.isBindingDisabled(name)) {
					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;
	}

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中

GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean – Integer)
byte – > file

@FunctionalInterfacepublic interface Converter<S, T>
在这里插入图片描述
在这里插入图片描述

未来我们可以给WebDataBinder里面放自己的Converter;
private static final class StringToNumber<T extends Number> implements Converter<String, T>

自定义 Converter

    //1、WebMvcConfigurer定制化SpringMVC的功能
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                // 不移除;后面的内容。矩阵变量功能就可以生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }

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

6、目标方法执行完成

将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。
在这里插入图片描述

7、处理派发结果

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

InternalResourceView@Override
	protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// Expose the model object as request attributes.
		exposeModelAsRequestAttributes(model, request);

		// Expose helpers as request attributes, if any.
		exposeHelpers(request);

		// Determine the path for the request dispatcher.
		String dispatcherPath = prepareForRendering(request, response);

		// Obtain a RequestDispatcher for the target resource (typically a JSP).
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(request, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including [" + getUrl() + "]");
			}
			rd.include(request, response);
		}

		else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to [" + getUrl() + "]");
			}
			rd.forward(request, response);
		}
	}
暴露模型作为请求域属性
// Expose the model object as request attributes.
		exposeModelAsRequestAttributes(model, request);
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);
			}
		});
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/107358.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Google 释出开源软件漏洞扫描工具 OSV-Scanner​

开源开发人员可在项目使用 OSV-Scanner&#xff0c;透过比对依赖项目和 OSV 漏洞资料库&#xff0c;找出项目的依赖项目中所存在的漏洞。Google 推出免费工具 OSV-Scanner&#xff08;https://github.com/google/osv-scanner&#xff09;&#xff0c;供开源开发人员可以更简单地…

docker网络模式 与 搭建nginx

目录 1. docker网络模式 2. 连接容器的三种方法 3. Docker Networking 3.1 创建网络 3.2 查看宿主机中创建的网络 3.3 删除网络 3.3 如何使用网络 4.搭建Nginx 1.准备工作 1.1 拉取镜像 1.2 在宿主机中创建挂载目录 2.准备2个tomcat 容器集群 3.准备 Nginx配置 3.…

ffmpeg 命令的简单使用

ffmpeg命令是在windows端使用的&#xff0c;使用前&#xff0c;需要先下载对应的 exe文件 1.准备环境 访问FFmpeg官网的下载地址&#xff08;https://www.gyan.dev/ffmpeg/builds/&#xff09;下载对应的压缩包&#xff0c;解压后即可使用 2.使用 ffmpeg.exe的使用 • 功能 …

利用Clion编译器完成C++的头文件与源文件的映射

1、前言 嘿嘿&#xff0c;众所周知&#xff0c;本人是一名Java后端人员&#xff0c;那么为什么开始搞C/C了咧&#xff1f; 因为Java是在C/C的基础上开发的语言&#xff0c;而且性能也是业界公认的除了机器语言外最好的编程语言&#xff0c;所以我就想啊&#xff0c;如果将Java…

记一次返工

记一次返工 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;记一次返工 CSDN&#xff1a;记一次返工 本文搬运自自己的博客园博客&#xff0c;发布于 2018-05-12 说明 本周我经历了参加工作以来&#xff0c;最大的一次返工&#xff0c;这一周都是茶饭不思…

安全灵活,华为云桌面成为数字化办公最佳搭档

目前云上数字化办公已经是大势所趋&#xff0c;但是如何快速高效地为用户提供便捷高效的工作和生活体验&#xff0c;依然需要大量的技术投入来实现。而华为云桌面就是云上办公的门户与平台&#xff0c;它可以将各种业务系统在云端进行集中管理与调度&#xff0c;通过统一的接口…

二、collection接口

文章目录Collection接口和常用方法(以ArrayList为例)基本使用方法遍历元素方式1(iterator)遍历元素方式2(增强for)练习Collection接口和常用方法(以ArrayList为例) 以ArrayList&#xff0c;其他集合同理使用 基本使用方法 注&#xff1a;集合添加基本数据类型会自动装箱成对…

非零基础自学Golang 第15章 Go命令行工具 15.1 编译相关指令 15.1.2 run

非零基础自学Golang 文章目录非零基础自学Golang第15章 Go命令行工具15.1 编译相关指令15.1.2 run第15章 Go命令行工具 15.1 编译相关指令 15.1.2 run 我们在调试代码时通常会使用go run命令。 该命令会编译执行Go语言源码&#xff0c;不会在当前目录生成可执行文件&#x…

【LeetCode每日一题:1799. N 次操作后的最大分数和~~~记忆化搜索+动态规划+状态压缩+最大公约数】

题目描述 给你 nums &#xff0c;它是一个大小为 2 * n 的正整数数组。你必须对这个数组执行 n 次操作。 在第 i 次操作时&#xff08;操作编号从 1 开始&#xff09;&#xff0c;你需要&#xff1a; 选择两个元素 x 和 y 。 获得分数 i * gcd(x, y) 。 将 x 和 y 从 nums 中…

07. 渗透测试之针对网站的信息收集

07. 渗透测试之针对网站的信息收集 01 信息收集简介 什么是信息收集 信息收集&#xff08;Information Gathering&#xff09;是指通过各种方式获取所需要的信息。信息收集是信息得以利用的第一步&#xff0c;也是关键的一步。信息收集工作的好坏&#xff0c;会影响整个渗透…

全国各城市疫情达峰进度条感染高峰时间表最新

防疫政策放开之后&#xff0c;多位专家就研判&#xff0c;未来一个多月内全国疫情将达到感染高峰。而近日&#xff0c;一张全国各地疫情进度和最终高峰的预计时间表流传&#xff0c;对各城市首轮感染高峰期进行了预测。那么&#xff0c;全国各城市疫情达峰进度条如何了&#xf…

怎么高效的开发一款成功的产品?Working Backwards

过去的几天一直在回顾整个产品团队过去一年所做的工作&#xff0c;有的工作有亮点&#xff0c;有的工作可以说是乏善可陈。对于不好的&#xff0c;发现其中的一个核心原因就是没有坚持“以终为始”的原则。现将我2021年10月写的一篇公司内部博客再次分享给团队&#xff0c;也分…

Simple Yet Effective Graph Contrastive Learning for Recommendation

1. 摘要 图神经网络(GNN)是一种强大的基于图的推荐系统学习方法。最近&#xff0c;结合对比学习的gnn在处理高度稀疏数据时&#xff0c;在数据增强方案的推荐方面表现出了优异的性能。尽管它们取得了成功&#xff0c;但大多数现有的图对比学习方法要么在用户-项目交互图上执行随…

JAVA零基础小白学习教程之day08_接口多态

day08-JAVAOOP 课程目标 1. 【理解】什么是接口 2. 【掌握】接口的定义格式 3. 【掌握】接口的使用 4. 【理解】接口的成员特点 5. 【理解】类和接口 抽象类和接口之间的关系 6. 【掌握】单继承多实现 7. 【理解】接口之间的多继承 8. 【掌握】接口的案例 9. 【理解】什么是…

一个程序员的新冠防护最佳实践

至今未阳&#xff0c;做了几次抗原检测都是阴性&#xff0c;所以把个人的防护经验给广大程序员朋友分享一下&#xff0c;尤其家里有小孩老人的可以参考一下。 我一天的防护操作 1、午餐 吃午饭时&#xff0c;走楼梯不去挤电梯&#xff0c;而且是在其他人吃完饭后&#xff0c…

Unreal Engine中调试常用方法

目录 常用调试方法 AddOnScreenDebugMessage UE_LOG&#xff1a;在控制台看调试信息 在蓝图中直接调用PrintString 自定义日志分类 声明 定义 简化日志输出的宏 日志格式化输出 常用调试方法 在虚幻引擎中常用的打印日志方法有三种&#xff0c;分别是&#xff1a;UE_…

C++运算符重载,匿名对象

目录 1、加号运算符重载 1.1 通过自己写成员函数&#xff0c;实现两个对象相加属性后返回新的对象 1.2通过成员函数实现加法运算符重载 1.3通过全局函数实现加法运算符重载&#xff0c;运算符重载也可以发生函数重载 1.4总结--对于内置的数据类型的表达式运算符是不可以改变…

Python+Requests实现接口自动化测试

一般对于自动化的理解&#xff0c;有两种方式的自动化。 第一&#xff0c;不需要写代码&#xff0c;完全由工具实现&#xff0c;这种方式的工具一般是公司自己研发的&#xff0c;方便黑盒测试人员使用。这种工具的特点是学习成本低&#xff0c;方便使用&#xff0c;但是通用性…

面向5G C-RAN组网的机房配置标准与模型

【摘 要】当前5G网络建设成本高,投资压力大,基站建设进度受铁塔公司制约;基站机房电费、租赁、服务费用逐年上升,运营维护压力大。面向未来5G建设,通过C-RAN组网,实现BBU集中化部署,可促进降本增效,实现低成本建网。首先分析了C-RAN区所处的网络位置,其次从BBU框多基…

java基于springboot的心理健康管理网站-计算机毕业设计

运行环境&#xff1a; 开发工具:IDEA /Eclipse 数据库:MYSQL5.7 应用服务:Tomcat7/Tomcat8 使用框架springbootvue 项目介绍 心灵治愈交流平台的主要使用者分为管理员和用户、心理咨询师&#xff0c;实现功能包括管理员&#xff1a;首页、个人中心、系统公告管理、用户管理、心…