springbootWeb开发,涉及静态资源的访问、请求参数处理


官方文档引用

By default, Spring Boot serves static content from a directory called
/static (or /public or /resources or /META-INF/resources) in the
classpath or from the root of the ServletContext. It uses the
ResourceHttpRequestHandler from Spring MVC so that you can modify that
behavior by adding your own WebMvcConfigurer and overriding the
addResourceHandlers method.

In a stand-alone web application, the default servlet from the
container is also enabled and acts as a fallback, serving content from
the root of the ServletContext if Spring decides not to handle it.
Most of the time, this does not happen (unless you modify the default
MVC configuration), because Spring can always handle requests through
the DispatcherServlet.

By default, resources are mapped on /, but you can tune that with
the spring.mvc.static-path-pattern property. For instance, relocating
all resources to /resources/
can be achieved as follows:
————————————————————————
spring.mvc.static-path-pattern=/resources/**
————————————————————————
You can also customize the static resource locations by using the
spring.web.resources.static-locations property (replacing the default
values with a list of directory locations). The root Servlet context
path, “/”, is automatically added as a location as well.

1 简单功能分析

在这里插入图片描述

1.1 静态资源的访问

  • 1.1.1访问目录

只要静态资源放在类路径(resources路径)下: called /static (or /public or /resources or /META-INF/resources
访问路径 : 当前项目根路径/ + 静态资源名http://localhost:8080/1.jpg
首先是访问controller,再看静态资源,如果还没有的话,就报404
在这里插入图片描述修改静态资源存放的路径:spring.web.resources.static-locations

  • 1.1.2访问前缀

由于项目中需要配置拦截器过滤掉静态资源,这时候可以在需要放行的静态资源上加前缀,springboot默认是无前缀的,加前缀的方法是:spring.mvc.static-path-pattern=/resources/**

  • 1.1.3webjars

就是把css和js文件弄成了jar包,在pom.xml引入依赖,网页就可以访问
官网地址:Webjars content
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径

1.2 欢迎页的支持

欢迎页位置:index.html的静态资源或者转到index页面的控制器
spring.mvc.static-path-pattern=/resources/**这个会使欢迎页失效

Spring Boot supports both static and templated welcome pages. It first looks for an index.html file in the configured static content locations. If one is not found, it then looks for an index template. If either is found, it is automatically used as the welcome page of the application.

1.3 静态资源配置原理

• SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
• SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效
看一个容器,首先就要看容器上的注解,什么时候才会生效

// WebMvcAutoConfiguration.class
// @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)全面接管springmvc定制化配置
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })//导入springmvc的包就会有
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {}
// WebMvcAutoConfigurationAdapter.class
@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class,
		org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
	//ResourceProperties resourceProperties;获取和spring.resources绑定的所有的值的对象
	//WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象
	//ListableBeanFactory beanFactory Spring的beanFactory
	//HttpMessageConverters 找到所有的HttpMessageConverters
	//ResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。=========
	//DispatcherServletPath  
	//ServletRegistrationBean   给应用注册Servlet、Filter....
	public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties,
			ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
			ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
			ObjectProvider<DispatcherServletPath> dispatcherServletPath,
			ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
		this.mvcProperties = mvcProperties;
		this.beanFactory = beanFactory;
		this.messageConvertersProvider = messageConvertersProvider;
		this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
		this.dispatcherServletPath = dispatcherServletPath;
		this.servletRegistrations = servletRegistrations;
		this.mvcProperties.checkConfiguration();
	}
}

• 配置文件的相关属性和xxx进行了绑定。WebMvcProperties->spring.mvc、ResourceProperties->spring.resources
• 可以发现WebMvcAutoConfigurationAdapter.class类只有一个有参构造器,那么有参构造器所有参数的值都会从容器中确定

1.3.1资源处理规则

@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
	super.addResourceHandlers(registry);
	if (!this.resourceProperties.isAddMappings()) {// 拿到spring.resources这个文件下配置的所有东西,isAddMappings是否禁用静态资源,禁用掉之后,所有静态资源都访问不了了
		logger.debug("Default resource handling disabled");
		return;
	}
	ServletContext servletContext = getServletContext();
	addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");  // 定义了访问前缀
	addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {  // 资源访问路径
		registration.addResourceLocations(this.resourceProperties.getStaticLocations());  // 资源访问位置
		if (servletContext ! = null) {
			registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
		}
	});
}
private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {
	addResourceHandler(registry, pattern, (registration) -> registration.addResourceLocations(locations));
}

private void addResourceHandler(ResourceHandlerRegistry registry, String pattern,
		Consumer<ResourceHandlerRegistration> customizer) {
	if (registry.hasMappingForPattern(pattern)) {
		return;
	}
	ResourceHandlerRegistration registration = registry.addResourceHandler(pattern);
	customizer.accept(registration);
	registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
	registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());  //配置静态资源缓存
	customizeResourceHandlerRegistration(registration);
}

1.3.2欢迎页的处理规则

// WebMvcAutoConfigurationAdapter.class
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
		FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
	WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
			new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
			this.mvcProperties.getStaticPathPattern());
	welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
	welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
	return welcomePageHandlerMapping;
}

//WelcomePageHandlerMapping.class->WelcomePageHandlerMapping
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
		ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
	if (welcomePage != null && "/**".equals(staticPathPattern)) {//解释了要用欢迎页功能,必须是/**
		logger.info("Adding welcome page: " + welcomePage);
		setRootViewName("forward:index.html");
	}
	else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {//欢迎页不存在就转到Controller处理
		logger.info("Adding welcome page template: index");
		setRootViewName("index");
	}
}

2 请求参数处理

2.1 请求映射

2.1.1rest使用与原理

请求映射:在@RestController的Controller类上,使用@xxxMapping注解声明方法能处理什么请求。
@RequestMapping:method、path、value
springboot最好使用rest风格开发
• Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
• 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
• 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户

请求
类型
后台UserController前端index.html运行
结果
GET在这里插入图片描述在这里插入图片描述GET-张三
POST在这里插入图片描述在这里插入图片描述POST-张三
PUT在这里插入图片描述在这里插入图片描述GET-张三
DELETE在这里插入图片描述在这里插入图片描述GET-张三

为什么会出现上述情况?发送的PUT和DELETE请求Controller处理的时候还是发送到get请求上去了?
看源码:
在这里插入图片描述

// WebMvcAutoConfiguration.class
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
// 请求过来被HiddenHttpMethodFilter拦截
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
	return new OrderedHiddenHttpMethodFilter();
}

@Bean
@ConditionalOnMissingBean(FormContentFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.formcontent.filter", name = "enabled", matchIfMissing = true)
public OrderedFormContentFilter formContentFilter() {
	return new OrderedFormContentFilter();
}
// FormContentFilter.class
private static final List<String> HTTP_METHODS = Arrays.asList("PUT", "PATCH", "DELETE");// 兼容以下请求;PUT.DELETE.PATCH
// OrderedHiddenHttpMethodFilter.class
public class OrderedHiddenHttpMethodFilter extends HiddenHttpMethodFilter implements OrderedFilter {}
// HiddenHttpMethodFilter.class
public static final String DEFAULT_METHOD_PARAM = "_method";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
		throws ServletException, IOException {
	HttpServletRequest requestToUse = request; // 原生request(post),不影响原生HttpServletRequest
},如PostMan直接发送Put、delete等方式请求,无需Filter。
	if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {// 请求正常并且是POST
		String paramValue = request.getParameter(this.methodParam);// 获取到_method的值。
		if (StringUtils.hasLength(paramValue)) {
			String method = paramValue.toUpperCase(Locale.ENGLISH);
			if (ALLOWED_METHODS.contains(method)) {
				requestToUse = new HttpMethodRequestWrapper(request, method);
			} // 包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
		}
	}
	filterChain.doFilter(requestToUse, response);
}

// HttpMethodRequestWrapper继承了HttpServletRequestWrapper
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
	private final String method;
	public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
		super(request);
		this.method = method;
	}
	@Override
	public String getMethod() {
		return this.method;// 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
	}
}
// HttpMethodRequestWrapper 
public class HttpServletRequestWrapper extends ServletRequestWrapper implements
        HttpServletRequest { // 实现了原生HttpServletRequest
}

前端修改后:

<body>
    <form action="/user" method="get">
        <input value="GET" type="submit">
    </form>
    <form action="/user" method="post">
        <input value="POST" type="submit">
    </form>
    <-- 表单只能提交get或者post情求,提交时带上name="_method" -->
    <form action="/user" method="post">
        <input name="_method" type="hidden" value="delete">
        <input value="DELETE" type="submit">
    </form>
    <form action="/user" method="post">
        <input value="PUT" type="submit">
    </form>
</body>
</html>
# application.yml
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: false

REST原理:
表单只能提交get或者post情求,提交时带上name="_method"
请求过来被HiddenHttpMethodFilter拦截
请求正常并且是POST
• 获取到_method的值。
• 兼容以下请求;PUT.DELETE.PATCH
• 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
• 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。

Rest使用客户端工具,
• 如PostMan直接发送Put、delete等方式请求,无需Filter。

自定义filter
根据@ConditionalOnMissingBean(HiddenHttpMethodFilter.class),可以设置自己的filter

<form action="/user" method="post">
	<input name="_m" type="hidden" value="delete">
	<input value="DELETE" type="submit">
</form>
package com.ll.controller;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;

@Configuration
public class MyFilter {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    }
}

2.1.2请求映射原理

所有的请求都会经过springmvc的DispatcherServlet,servlet必须重写doGet,doPost方法。
继承树:
在这里插入图片描述DispatcherServlet里面的doGet,doPost——>调用到FrameworkServlet里面的doGet,doPost——>里面会调用processRequest(request, response);——>processRequest里调用doService(request, response);——>最终在DispatcherServlet里面实现了doService——>doDispatch(request, response);
所以springmvc的方法分析都从DispatcherServlet.doDispatch(request, response);开始。

// DispatcherServlet.class
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);

			// Determine handler for the current request.
			mappedHandler = getHandler(processedRequest);  //找到当前请求使用哪个Handler(Controller)的方法处理
			}
		}
}

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

5个handlerMappings,处理器映射
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。项目开启时自动扫描所有的包,把映射规则放入到这个里面
WelcomePageHandlerMapping:欢迎页的映射规则
在这里插入图片描述
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则,所有的请求映射都在HandlerMapping中。
在这里插入图片描述

springmvc要求不能有同名的请求,会做一个最佳请求处理
在这里插入图片描述• SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
• SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
• 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping
• 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping

2.2普通参数与基本注解

2.2.1注解

@PathVariable、@RequestHeader、@RequestParam、@CookieValue、@RequestBody
@ModelAttribute
@MatrixVariable矩阵变量应该绑定在路径变量中
/cars/{path}?xxx=xxx&XXX=XXX 请求参数,查询字符串,用@RequestParam取得
/cars/path;a=1;b=2,3 矩阵变量

矩阵变量的应用:
如果页面禁用cookie,session里面的值应该怎么使用?
url重写:/abc;jsessionid=xxx 把cookie值使用矩阵变量的方式进行传递

@RestController
public class ParameterTestController {


    //  car/2/owner/zhangsan
    @GetMapping("/car/{id}/owner/{username}")
    public Map<String,Object> getCar(
                                     // 路径变量
                                     @PathVariable("id") Integer id,
                                     @PathVariable("username") String name,
                                     @PathVariable Map<String,String> pv,// 可以把路径变量上所有的值整个返回一个Map,Map的key和value必须为String
                                     
                                     // 请求头
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,

                                     // 请求参数
                                     @RequestParam("age") Integer age,
                                     @RequestParam("inters") List<String> inters,
                                     @RequestParam Map<String,String> params,
                                     
                                     // cookie的值
                                     @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
    // 路径变量里有两个相同的value时
    @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;

    }

}

开启矩阵变量:
方法1
在这里插入图片描述
方法2
在这里插入图片描述

2.2.2Servlet 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);
	}

2.2.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();

Map、Model类型的参数,会返回 mavContainer.getModel();—> BindingAwareModelMap 是Model 也是Map
mavContainer.getModel(); 获取到值的

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

2.2.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.3、参数处理原理

• HandlerMapping中找到能处理请求的Handler(Controller.method())
• 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter

// DispatcherServlet.java
				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

• 适配器执行目标方法并确定方法参数的每一个值

2.3.1HandlerAdapter

// DispatcherServlet.java
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				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");
	}

在这里插入图片描述
0 - 支持方法上标注@RequestMapping
1 - 支持函数式编程的

2.3.2执行目标方法

// DispatcherServlet.java
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// RequestMappingHandlerAdapter.java
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
// RequestMappingHandlerAdapter.java
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// ServletInvocableHandlerMethod.invokeAndHandle(webRequest, mavContainer).java
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 执行当前请求,断点到controller里面执行请求去了,真正执行目标方法

// 真正执行目标方法需要确定目标方法里每一个参数的值
// InvocableHandlerMethod.java
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);// 获取方法中所有参数的值

2.3.3参数解析器-HandlerMethodArgumentResolver

确定将要执行的目标方法的每一个参数的值是什么;
SpringMVC目标方法能写多少种参数类型。取决于参数解析器。

// RequestMappingHandlerAdapter.java
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}

在这里插入图片描述
HandlerMethodArgumentResolver.java
是否支持supportsParameter,如果支持就用这种参数解析
支持就调用 resolveArgument解析
在这里插入图片描述

2.3.4返回值处理器

// RequestMappingHandlerAdapter.java
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}

在这里插入图片描述

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

获取方法中所有参数的值

// InvocableHandlerMethod.java
	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;
	}
2.3.5.1挨个判断所有参数解析器那个支持解析这个参数
// InvocableHandlerMethod.java
// 判断哪个参数解析器能解析这个参数
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}

// HandlerMethodArgumentResolverComposite.java
	@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;
	}
2.3.5.2解析这个参数的值
// InvocableHandlerMethod.java
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);

// HandlerMethodArgumentResolverComposite.java

2.3.5.3自定义类型参数封装POJO

2.3.6目标方法执行完成

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

2.3.7处理派发结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值