# 04.SpringBoot-Web开发-请求处理
# 1.简介
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver bea
- 内容协商视图解析器和BeanName视图解析器
Support for serving static resources, including support for WebJars (covered later in this document))
- 静态资源
Automatic registration of
Converter,GenericConverter, andFormatterbeans.- 自动注册
Converter,GenericConverter,Formatter
- 自动注册
Support for HttpMessageConverters (covered later in this document).
- 支持 HttpMessageConverters (后来我们配合内容协商理解原理)
Static index.html support.
- 静态index.html 页支持
Custom Favicon support (covered later in this document)
- 自定义 Favicon
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document)
- 自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)
# 2.静态资源
1.静态资源目录
只要静态资源放在类路径下: /static or /public or /resources or/META-INF/resources
访问:项目根路径+静态资源名称
原理:静态映射/**
拦截静态资源和所有请求,但是,请求进来会先去找controller,看有没有匹配的路径,如果没有交给静态处理器进行处理
也可以改变默认的静态资源存访目录,只有此目录下的文件才能够被访问
spring:
web:
resources:
static-locations: [classpath:/xuxu/]
2
3
4
2.静态资源访问前缀
spring:
web:
resources:
static-locations: [classpath:/xuxu/]
mvc:
static-path-pattern: /resources/**
2
3
4
5
6
当前项目 + static-path-pattern + 静态资源名
3.webjar
可用jar方式添加css,js等资源文件
https://www.webjars.org/ (opens new window)
例如,添加jquery
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
2
3
4
5
自动映射,根据依赖里面的包路径访问
localhost:8888/webjars/jquery/3.5.1/jquery.js (opens new window)
# 3.欢迎页和小图标
1.欢迎页支持
静态资源路径下 index.html。
- 可以配置静态资源路径
- 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
2.自定义Favicon
指网页标签上的小图标。
favicon.ico 放在静态资源目录下即可。
# 4.静态资源原理
- SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
- SpringMVC功能的自动配置类
WebMvcAutoConfiguration,生效(这些类都存在)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
...
}
2
3
4
5
6
7
8
9
10
- 给容器中配置的内容:
- 配置文件的相关属性的绑定:WebMvcProperties对应配置文件spring.mvc下的内容、ResourceProperties==spring.resources
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
...
}
2
3
4
5
6
7
配置类中只有一个有参构造器,构造器所有参数都在容器中获取
public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = webProperties.getResources();
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = (ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
this.mvcProperties.checkConfiguration();
}
2
3
4
5
6
7
8
9
10
ResourceProperties resourceProperties;获取和spring.resources绑定的所有的值的对象
WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象
ListableBeanFactory beanFactory Spring的beanFactory
HttpMessageConverters 找到所有的HttpMessageConverters
ResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。
DispatcherServletPath
ServletRegistrationBean 给应用注册Servlet、Filter
资源处理的默认规则
...
public class WebMvcAutoConfiguration {
...
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
...
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
if (!this.resourceProperties.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));
}
});
}
...
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
根据上述代码,我们可以同过配置禁止所有静态资源规则。
spring:
web:
resources:
add-mappings: false
2
3
4
静态资源规则:
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/].
*/
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
...
}
2
3
4
5
6
7
8
9
10
11
12
13
欢迎页的处理规则
...
public class WebMvcAutoConfiguration {
...
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
...
@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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 /index
logger.info("Adding welcome page template: index");
setRootViewName("index");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
这构造方法内的代码也解释了web场景-welcome与favicon功能中配置static-path-pattern了,welcome页面和小图标失效的问题。
# 5.请求处理-Rest映射及源码解析
1.用法
- 开启页面表单的Rest功能
- 页面 form的属性method=post,隐藏域 _method=put、delete等(如果直接get或post,无需隐藏域)
- 编写请求映射
spring:
mvc:
hidden method:
filter:
enabled: true
2
3
4
5
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lalla</title>
</head>
<body>
<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 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>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@GetMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
}
@PostMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
}
@PutMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
}
@DeleteMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2.原理
1)表单提交带上_method=PUT
2)拦截器HiddenHttpMethodFilter拦截
请求正常且是POST
获取_method的值(兼容PUT,DELETE,PATCH)
原生request(post),包装模式requestWrapper重写了getMethod方法,返回的是传入的值
过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
//调用HttpMethodRequestWrapper的构造方法
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
//过滤后传入的是requestToUser,它实际上是HttpMethodRequestWrapper对象
//所以以后的getMethod也是它
filterChain.doFilter((ServletRequest)requestToUse, response);
}
static {
ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
//调用父类的request,父类里面再调用父类,返回的还是传入的request,
//所以是同一次请求
super(request);
this.method = method;
}
public String getMethod() {
return this.method;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
3.Rest使用客户端工具
- 如PostMan可直接发送put、delete等方式请求。
补充:包装模式
| 包装模式核心角色 | 代码中的实现 | 作用说明 |
|---|---|---|
| 抽象组件(Component) | HttpServletRequest 接口 | 定义了 HTTP 请求的核心方法(如 getMethod()),是被包装的「核心接口」; |
| 具体组件(ConcreteComponent) | 原生的 HttpServletRequest 实例(Tomcat 等容器创建的对象) | 实现了 HttpServletRequest 接口,是「基础对象」,提供默认的 getMethod() 实现(返回真实请求方法,如 POST); |
| 抽象包装类(Decorator)(不一定是抽象类) | HttpServletRequestWrapper 类 | 实现了 HttpServletRequest 接口,且持有 HttpServletRequest 引用(通过父类构造器传入),是所有请求包装类的「抽象包装基类」; |
| 具体包装类(ConcreteDecorator) | HttpMethodRequestWrapper 内部类 | 继承 HttpServletRequestWrapper(抽象包装类),重写 getMethod() 方法,动态修改返回值(核心:添加「方法伪装」的额外功能); |
用 HttpMethodRequestWrapper 包装 HttpServletRequest,重写特定方法实现功能扩展,不修改原生对象;
# 6.请求处理-怎么改变默认的_method
@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnProperty(
prefix = "spring.mvc.hiddenmethod.filter",
name = {"enabled"}
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
2
3
4
5
6
7
8
9
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})意味着没有这个HiddenHttpMethodFilter才会注册到容器中,因此我们可以自己注册一个HiddenHttpMethodFilter到容器中,并修改他的属性名
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("_m");
return hiddenHttpMethodFilter;
}
2
3
4
5
6
# 7.请求处理-请求映射原理
前置知识,抽象类和子类的调用方式
请求 → 框架/代码拿到「抽象类类型的子类实例」 → 调用抽象类定义的方法 → 多态触发子类的具体实现

HttpServlet是一个抽象类,FramworkServlet是具体实现
请求调用HttpServlet的service方法→子类继承父类的基础方法再添加自己的处理
先转为HttpServletRequest

到子类处理

调用父类的最后一句this.service(request,response)跳转到父类

前置知识:只有抽象类子类没有重写方法时,才会执行父类的默认实现;子类重写后,优先执行子类实现。
所以这里的this.get调用的是子类FrameworkServlet的doget方法
随后进行processRequest
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.processRequest(request, response);
}
2
3
doservice,FrameworkServlet里面的doService是抽象方法,所以直接调用子类doService,再调用doDispatch();

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
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
this.handlerMappings在Debug模式下展现的内容:

其中,保存了所有@RequestMapping 和handler的映射规则。

所有的请求映射都在HandlerMapping中:
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。如果有就找到这个请求对应的handler如果没有就是下一个 HandlerMapping
我门需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
最后映射到controller,返回结果
# 8.请求处理-常用参数注解使用
注解:
@PathVariable 路径变量 @RequestHeader 获取请求头 @RequestParam 获取请求参数(指问号后的参数,url?a=1&b=2) @CookieValue 获取Cookie值 @RequestAttribute 获取request域属性 @RequestBody 获取请求体[POST] @MatrixVariable 矩阵变量 @ModelAttribute
示例:
@RestController
public class ParameterTestController {
@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,
@RequestParam("age") Integer age,
@RequestParam("inters") List<String> inters,
@RequestParam Map<String,String> params,
@CookieValue("Idea-52388292") Cookie cookie
){
HashMap<String, Object> map = new HashMap<>();
// map.put("id",id);
// map.put("name",name);
// map.put("pv",pv);
// map.put("userAgent",userAgent);
map.put("age",age);
map.put("inters",inters);
map.put("params",params);
map.put("cookie",cookie);
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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
RequestAttribute示例
@Controller
public class RequestAttributeController {
@GetMapping("/goto")
public String goToPage(HttpServletRequest request){
request.setAttribute("msg","这是从goToPage controller传入的");
return "forward:/success";
}
@GetMapping("/success")
@ResponseBody
public Map<String,Object> success(@RequestAttribute(value = "msg") String msg
, HttpServletRequest request){
Object msg1 = request.getAttribute("msg");
HashMap<String, Object> map = new HashMap<>();
map.put("msg",msg);
map.put("msg_1",msg1);
return map;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MatrixVariable 矩阵变量
语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
1)SpringBoot默认是禁用了矩阵变量的功能
2)手动开启:原理。对于路径的处理。UrlPathHelper的removeSemicolonContent设置为false,让其支持矩阵变量的。
3)矩阵变量必须有url路径变量才能被解析
手动开启矩阵变量
@Configuration
public class webConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 不移除;后面的内容。矩阵变量功能就可以生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
2
3
4
5
6
7
8
9
10
@MatrixVariable的用例
//cars/sell;low=34;brand=byd,audi,yd
@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
@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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 9.请求处理-各种类型参数解析原理
大体思路:HandlerMapping找能处理请求的Handler,找适配器 HandlerAdapter ,找参数解析器-HandlerMethodArgumentResolver,解析返回
1.从DispatcherServlet开始说起:
public class DispatcherServlet extends FrameworkServlet {
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);
// 获取当前请求的映射,根据5种类型进行映射
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 获取映射适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
HandlerMapping中找到能处理请求的Handler(Controller.method())。
为当前Handler 找一个适配器 HandlerAdapter(默认会加载所有
HandlerAdapter),用的最多的是RequestMappingHandlerAdapter。适配器执行目标方法并确定方法参数的每一个值。
2.4种类型的映射适配器,看是否支持该类型适配器,支持就返回

3.执行目标方法(以下内容都在此方法种)
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = null;
...
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
//本节重点
// 适配器处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
HandlerAdapter接口实现类RequestMappingHandlerAdapter(主要用来处理@RequestMapping)
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
...
//AbstractHandlerMethodAdapter类的方法,RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapter
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//handleInternal的核心
mav = invokeHandlerMethod(request, response, handlerMethod);//解释看下节
//...
return mav;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
4.参数解析器解析参数
参数解析器介绍:
确定将要执行的目标方法的每一个参数的值是什么;
SpringMVC目标方法能写多少种参数类型。取决于参数解析器argumentResolvers。

有27种类型,比如说RequestParam方法参数解析器、PathVariable方法参数解析器等
若是无注解的则使用以下参数解析器
| 无注解参数类型 | 核心解析器 |
|---|---|
| 基础类型(int/long/String/boolean 等) | RequestParamMethodArgumentResolver |
| 自定义对象(如 User/Order) | ServletModelAttributeMethodProcessor |
| Servlet 原生对象(HttpServletRequest/HttpServletResponse/HttpSession) | ServletRequestMethodArgumentResolver |
| Model/Map/ModelMap | ModelMethodArgumentResolver |
| MultipartFile/Part | RequestPartMethodArgumentResolver |
| 其他特殊类型(Locale/TimeZone) | LocaleMethodArgumentResolver 等 |
这一步之前初始化了所有参数解析器
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
@Override
public void afterPropertiesSet() {
...
if (this.argumentResolvers == null) {//初始化argumentResolvers
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
...
}
//初始化了一堆的实现HandlerMethodArgumentResolver接口的
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
if (KotlinDetector.isKotlinPresent()) {
resolvers.add(new ContinuationHandlerMethodArgumentResolver());
}
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
HandlerMethodArgumentResolverComposite类如下:(众多参数解析器argumentResolvers的包装类)。
如果没有这个「包装类」,InvocableHandlerMethod 就需要逐个遍历所有解析器、判断是否支持当前参数,代码会极其冗余;而 HandlerMethodArgumentResolverComposite 把这些逻辑封装起来,对外提供简洁的接口,这正是「包装 / 组合模式」的核心思想。

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
...
//注册解析器(把所有解析器加入集合)
public HandlerMethodArgumentResolverComposite addResolvers(
@Nullable HandlerMethodArgumentResolver... resolvers) {
if (resolvers != null) {
Collections.addAll(this.argumentResolvers, resolvers);
}
return this;
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
回顾执行目标方法
public class DispatcherServlet extends FrameworkServlet {
...
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = null;
...
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
2
3
4
5
6
RequestMappingHandlerAdapter的handle()方法:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
...
//AbstractHandlerMethodAdapter类的方法,RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapter
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//handleInternal的核心
mav = invokeHandlerMethod(request, response, handlerMethod);//解释看下节
//...
return mav;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
invokeAndHandle()方法如下:
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
...
try {
//returnValue存储起来
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
...
}
}
@Nullable//InvocableHandlerMethod类的,ServletInvocableHandlerMethod类继承InvocableHandlerMethod类
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
////获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
...
return doInvoke(args);
}
@Nullable
protected Object doInvoke(Object... args) throws Exception {
Method method = getBridgedMethod();//@RequestMapping的方法
ReflectionUtils.makeAccessible(method);
try {
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
}
//通过反射调用
return method.invoke(getBean(), args);//getBean()指@RequestMapping的方法所在类的对象。
}
catch (IllegalArgumentException ex) {
...
}
catch (InvocationTargetException ex) {
...
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
如何确定目标方法每一个参数的值
重点分析ServletInvocableHandlerMethod的getMethodArgumentValues方法
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
...
@Nullable//InvocableHandlerMethod类的,ServletInvocableHandlerMethod类继承InvocableHandlerMethod类
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
////获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
...
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[] 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;
}
//查看resolvers是否有支持
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) {
....
}
}
return args;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
this.resolvers的类型为HandlerMethodArgumentResolverComposite
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
@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);
}
@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);//找到了,resolver就缓存起来,方便稍后resolveArgument()方法使用
break;
}
}
}
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
总结:
ha.handle(processedRequest, response, mappedHandler.getHandler());内部方法类关系总结
1.RequestMappingHandlerAdapter 初始化时加载所有「参数解析器(HandlerMethodArgumentResolver)」并封装到 HandlerMethodArgumentResolverComposite
2.执行时将解析器集合传递给 InvocableHandlerMethod
3.InvocableHandlerMethod 委托 HandlerMethodArgumentResolverComposite 找到「适配当前参数类型的解析器」(比如 RequestParamMethodArgumentResolver)
4.最终由具体解析器(如 RequestParamMethodArgumentResolver)继承 AbstractNamedValueMethodArgumentResolver 完成参数的「命名值类型参数(@RequestParam/@RequestHeader 等)」统一处理
5.解析结果返回给 InvocableHandlerMethod 执行控制器方法。
6.最终返回给ha.handle(processedRequest, response, mappedHandler.getHandler())
示例
以 @RequestParam("name") String name 为例,解析流程:
RequestMappingHandlerAdapter触发 handle 方法,创建InvocableHandlerMethod,并把「所有解析器组成的 Composite」传给它;InvocableHandlerMethod遍历控制器方法的参数(如name参数),调用Composite.supportsParameter(参数);Composite遍历所有解析器,找到RequestParamMethodArgumentResolver(它的supportsParameter返回 true);Composite调用RequestParamMethodArgumentResolver.resolveArgument(参数, request);RequestParamMethodArgumentResolver继承AbstractNamedValueMethodArgumentResolver,执行通用解析逻辑:- 解析
@RequestParam注解的属性(name = "name"、required = true、defaultValue = ""); - 从
request.getParameter("name")取值; - 处理默认值(如果没传值且有默认值则用默认值);
- 类型转换(把 String 转为目标类型);
- 解析
解析后的参数值返回给
InvocableHandlerMethod;InvocableHandlerMethod用反射执行控制器方法,传入解析后的参数值。
# 10.请求处理-Servlet API参数解析原理
- WebRequest
- ServletRequest
- MultipartRequest
- HttpSession
- javax.servlet.http.PushBuilder
- Principal
- InputStream
- Reader
- HttpMethod
- Locale
- TimeZone
- ZoneId
ServletRequestMethodArgumentResolver用来处理以上的参数
# 11.请求处理-Model、Map原理
Model和Map是「同一数据的不同接口封装」:控制器方法中声明Model或Map参数,Spring 会注入同一个BindingAwareModelMap实例(Map的子类 +Model接口的实现类);- 所有存入
Model/Map的数据,最终都会被归集到ModelAndViewContainer的Model容器中; - 请求处理完成后,
Model中的数据会被暴露为视图的「请求域属性」(request.setAttribute),供页面渲染使用。
复杂参数:
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<String,Object> map与Model model用什么参数处理器。
Map<String,Object> map参数用MapMethodProcessor处理:
public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (Map.class.isAssignableFrom(parameter.getParameterType()) &&
parameter.getParameterAnnotations().length == 0);
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
return mavContainer.getModel();
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mavContainer.getModel()如下:
public class ModelAndViewContainer {
...
private final ModelMap defaultModel = new BindingAwareModelMap();
@Nullable
private ModelMap redirectModel;
...
public ModelMap getModel() {
if (useDefaultModel()) {
return this.defaultModel;
}
else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
private boolean useDefaultModel() {
return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Model model用ModelMethodProcessor处理:
public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return Model.class.isAssignableFrom(parameter.getParameterType());
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
return mavContainer.getModel();
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
return mavContainer.getModel();这跟MapMethodProcessor的一致
Model也是另一种意义的Map。
接下来看看Map<String,Object> map与Model model值是如何做到用request.getAttribute()获取的。
众所周知,所有的数据都放在 ModelAndView包含要去的页面地址View,还包含Model数据。
先看ModelAndView接下来是如何处理的?
public class DispatcherServlet extends FrameworkServlet {
...
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
try {
ModelAndView mv = null;
...
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//处理分发结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
...
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
...
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
...
}
...
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
...
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
view.render(mv.getModelInternal(), request, response);
...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
在Debug模式下,view属为InternalResourceView类。
public class InternalResourceView extends AbstractUrlBasedView {
@Override//该方法在AbstractView,AbstractUrlBasedView继承了AbstractView
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
...
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
//看下一个方法实现
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
@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);
...
}
//该方法在AbstractView,AbstractUrlBasedView继承了AbstractView
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
HttpServletRequest request) throws Exception {
model.forEach((name, value) -> {
if (value != null) {
request.setAttribute(name, value);
}
else {
request.removeAttribute(name);
}
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
exposeModelAsRequestAttributes方法看出,Map<String,Object> map,Model model这两种类型数据可以给request域中放数据,用request.getAttribute()获取。
# 12.请求处理-自定义参数绑定
核心逻辑:WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
在过程当中,用到GenericConversionService:在设置每一个值的时候,找它里面的所有converter,可以将这个数据类型(request带来参数的字符串)转换到指定的类型
public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
@Override//本方法在ModelAttributeMethodProcessor类,
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
@Override
@Nullable//本方法在ModelAttributeMethodProcessor类,
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
...
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 (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)) {
//web数据绑定器,将请求参数的值绑定到指定的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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 完整流程:
# 1. 请求入口:DispatcherServlet
当一个HTTP请求到达时,DispatcherServlet的doDispatch方法开始处理。它会根据请求路径找到可以处理该请求的处理器(Handler),也就是我们写的Controller方法。这个处理器和方法的所有信息(包括参数列表、注解等)被封装成一个HandlerMethod对象。
# 2. 获取参数解析器:HandlerMethodArgumentResolver
Spring需要决定由谁来负责将请求数据绑定到这个HandlerMethod的每一个参数上。这个决定是在HandlerMethodArgumentResolverComposite(参数解析器组合器)中完成的。
它内部维护了一个参数解析器列表(
List<HandlerMethodArgumentResolver>),这个列表包含了Spring内置的以及我们自定义添加的所有解析器。对于每个方法参数,它会遍历这个列表,调用每个解析器的
supportsParameter(MethodParameter parameter)方法,来判断该解析器是否“支持”处理当前参数。
# 3. 常见的参数解析器示例
如果参数上有
@RequestParam注解,那么RequestParamMethodArgumentResolver会返回true。如果参数是
Model类型,ModelMethodProcessor会返回true。如果参数是自定义的
User对象且没有注解,Spring默认会将其视为需要从表单提交的数据中绑定属性,这由ModelAttributeMethodProcessor处理(但它通常配合@ModelAttribute显式使用,或不加注解时用于简单类型绑定的兜底,而复杂对象绑定则需要ServletModelAttributeMethodProcessor,这取决于配置)。
# 4. 执行参数解析:绑定数据
找到合适的解析器后,DispatcherServlet会调用它的resolveArgument方法,这是数据绑定的核心。这个方法需要完成几件事:
从请求中获取数据:从
HttpServletRequest中获取参数值(例如,通过request.getParameter("name"))。类型转换:将获取到的字符串转换为参数需要的类型(例如,将String "18" 转换为Integer 18)。这背后依赖于Spring的类型转换体系(
ConversionService)。数据绑定:如果参数是对象(如
User),它会创建一个新的User实例,并通过数据绑定器(WebDataBinder),将请求中的同名参数(如name=张三&age=18)一一绑定到对象的对应属性上。校验:如果参数上标记了
@Valid或@Validated注解,WebDataBinder还会触发数据校验,并将校验结果封装到紧随其后的BindingResult参数中。
# 13.请求处理-自定义消息解析器
未来我们可以在WebDataBinder里放自己的Convert
例如:
@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) {
if (!source.isEmpty()){
String[] split = source.split(",");
Pet pet = new Pet();
pet.setName(split[0]);
pet.setAge(split[1]);
return pet;
}
return null;
}
});
}
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22