【springMVC】springMVC学习系列一:springMVC的组件

发布于:2025-05-28 ⋅ 阅读:(27) ⋅ 点赞:(0)

系列文章目录

前言

spring mvc 它解决了什么问题呢?
1.URL映射
2. 表单参数映射
3. 调用目标Control
4. 数据模型映射
5. 视图解析
6. 异常处理
上述解决在spring mvc 中都体现在如下组件当中
HandlerMapping: url与控制器的映谢
HandlerAdapter: 控制器执行适配器
ViewResolver:视图仓库
view:具体解析视图
HandlerExceptionResolver:异常捕捕捉器
HandlerInterceptor:拦截器

各组件调用关系流程图如下:
在这里插入图片描述

一、handler

真正处理请求的对象,有4种
1)实现了Controller接口的Bean对象
2)实现了HttpRequestHandler接口的Bean对象
3)@Controller注解的类中添加了@RequestMapping注解的方法(HandlerMethod)
4)HandlerFunction对象

其实handler是一个逻辑概念,并没有一个统一的接口,上面4中对象都可以成为handler,代码中是用Object来引用的,后面一篇会具体介绍

二、handlerMapping

有3种
1)BeanNameUrlHandlerMapping:负责Controller接口和HttpRequestHandler接口
2)RequestMappingHandlerMapping:负责@RequestMapping的方法
3)RouterFunctionMapping:负责RouterFunction以及其中的HandlerFunction

1.handlerMapping的重要结构

1)存储请求和handler的映射关系
例如RequestMappingHandlerMapping存储了2个map

a. mappingLookup:key:RequestMappingInfo value:HandlerMethod
RequestMappingInfo 是对@RequestMapping注解中元数据的封装
HandlerMethod 是对method和目标对象的封装,是处理器真正执行的地方

b. urlLookup:key:url value:List
url和RequestMappingInfo是多对多的关系,比如@RequestMapping 注解可以配置多个URL路径来映射到同一个控制器方法

@Controller
public class MyController {
    
    @RequestMapping(value = {"/path1", "/path2", "/path3"})
    public String handleMultiplePaths() {
        return "viewName";
    }
}

一个URL可以通过请求方式映射多个控制器方法

@Controller
public class MyController {
    
    @RequestMapping(value = "/path",method = "GET")
    public String getResource() {
        return "viewName";
    }
    @RequestMapping(value = "/path",method = "POST")
    public String postResource() {
        return "viewName";
    }
}

至于具体怎么通过这2个map找到对应的处理器,下一篇再讲

2)存储拦截器
在处理请求时,与handler一起组成执行链executionChain

2.如何获取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;

三、handlerAdapter

不同种类的Handler处理请求的方法不一样,那么需要一个能将他们统一调用的东西,那就是适配器
针对不同的Handler,会有不同的适配器:
HttpRequestHandlerAdapter:对应实现了HttpRequestHandler的处理器
SimpleControllerHandlerAdapter:对应实现了Controller接口的处理器
RequestMappingHandlerAdapter:对应@RequestMapping注解方法的处理器
HandlerFunctionAdapter:对应实现了 HandlerFunction接口的处理器

寻找适配器的逻辑如下,也是策略模式

rotected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 
    if (this.handlerAdapters != null) { 
        for (HandlerAdapter adapter : this.handlerAdapters) { 
            if (adapter.supports(handler)) { 
                return adapter; 
            } 
        } 
    } 
  。。。
 }

四、HandlerMethodArgumentResolver

SpringMVC要去解析方法参数,那么该如何解析呢,是通过HandlerMethodArgumentResolver来进行的,比如对于@RequestMapping方法
RequestParamMethodArgumentResolver:负责处理@RequestParam
RequestHeaderMethodArgumentResolver:负责处理@RequestHeader
SessionAttributeMethodArgumentResolver:负责处理@SessionAttribute
RequestAttributeMethodArgumentResolver:负责处理@RequestAttribute
RequestResponseBodyMethodProcessor:负责处理@RequestBody

而在判断某个参数该由哪个HandlerMethodArgumentResolver处理时,也是用的策略模式:

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

五、HandlerMethodReturnValueHandler

方法返回值如何解析呢,比如返回一个String,加了@ResponseBody注解直接将这个String返回给浏览器,没有加就根据这个String找到对应的页面,把页面返回给浏览器。这些处理是通过HandlerMethodReturnValueHandler来进行的,比如下面的实现:
1)RequestResponseBodyMethodProcessor:处理加了@ResponseBody注解的情况
2)ViewNameMethodReturnValueHandler:处理没有加@ResponseBody注解并且返回值类型为String的情况
3)ModelMethodProcessor:处理返回值是Model类型的情况

如何判断使用哪个来解析呢,还是策略模式:

private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}

六、HttpMessageConverter

RequestResponseBodyMethodProcessor,因为它会处理加了@ResponseBody注解的情况,也是目前我们用得最多的情况。如果@ResponseBody注解的方法返回的不是String,而是Map、User对象呢,该怎么解析呢?
SpringMVC会利用HttpMessageConverter来处理,默认情况下有4种:
1)ByteArrayHttpMessageConverter:处理返回值为字节数组的情况,把字节数组返回给浏览器
2)StringHttpMessageConverter:处理返回值为字符串的情况,把字符串按指定的编码序列号后返回给浏览器
3)SourceHttpMessageConverter:处理返回值为XML对象的情况,比如把DOMSource对象返回给浏览器
4)AllEncompassingFormHttpMessageConverter:处理返回值为MultiValueMap对象的情况

不过以上四个Converter是不能处理Map对象或User对象的,所以如果返回的是Map或User对象,那
么得单独配置一个Converter,比如MappingJackson2HttpMessageConverter,这个Converter比
较强大,能把String、Map、User对象等等都能转化成JSON格式。

public class AppConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) { 
  MappingJackson2HttpMessageConverter messageConverter = new 
MappingJackson2HttpMessageConverter();
  messageConverter.setDefaultCharset(StandardCharsets.UTF_8); 
  converters.add(messageConverter);} 
 }

另外,如果使用StringHttpMessageConverter,字符集默认为ISO-8859-1,所以默认情况下返
回中文会乱码,需要通过配置解决

ublic class AppConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) { 7
  StringHttpMessageConverter messageConverter = new 
StringHttpMessageConverter();
 
  messageConverter.setDefaultCharset(StandardCharsets.UTF_8); 
  converters.add(messageConverter);} 
 }

网站公告

今日签到

点亮在社区的每一天
去签到