SpringMVC

DispatcherServlet 初始化时机

默认情况下,DispatcherServlet在Tomcat服务器第一次使用到它时初始化,可以通过参数修改这个策略。

//方法参数默认为-1,大于0表示容器启动后立即初始化DispatcherServlet
DispatcherServletRegistrationBean.setLoadOnStartup(int);

配置文件中对应的参数为

spring.mvc.servlet.load-on-startup

DispatcherServlet 初始化做了什么

可以在 DispatcherServlet.onRefresh(..) 方法中看到其具体实现

public class DispatcherServlet extends FrameworkServlet {
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);	//文件上传相关
		initLocaleResolver(context);	//初始化本地化信息,国家,地区,语言等
		initThemeResolver(context);
		initHandlerMappings(context);	//初始化请求路径与控制器方法的映射关系
		initHandlerAdapters(context);	//请求处理逻辑的适配器,适配不同形式的控制器方法,最常见的就是直接将逻辑写在Controller的方法中
		initHandlerExceptionResolvers(context);		//控制器异常解析(统一异常处理?)
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}
}

RequestMappingHandlerMapping 基本用途

根据@RequestMapping及其衍生注解实现路径映射

  1. 找到当前路径下所有的控制器类
  2. 找到控制器类中所有被@RequestMapping等注解标注的方法
  3. 记录这些注解的值路径与对应的方法,生成映射关系,存储在RequestMappingHandlerMapping
//可以通过这个方法来获取映射结果
//RequestMappingInfo 封装了请求参数等信息
//HandlerMethod 封装了控制器方法信息
Map<RequestMappingInfo, HandlerMethod> handlerMethods = RequestMappingHandlerMapping.getHandlerMethods();

//请求来了,获取控制器方法,返回处理器执行链对象,此对象除了包含 HandlerMethod,还包含了过滤器(如果有)
HandlerExecutionChain chain = RequestMappingHandlerMapping.getHandler(HttpServletRequest);

RequestMappingHandlerAdapter 基本用途

调用用 @RequestMapping 标注的控制器方法

//核心:调用这个方法,其内部最终会通过反射调用控制器方法
RequestMappingHandlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod)chain.getHandler());

自定义参数和返回值处理器

Controller中请求方法的参数是由参数解析器来完成解析的,位置如下

public class RequestMappingHandlerAdapter
	
	//获取所有的参数解析器
	public List<HandlerMethodArgumentResolver> getArgumentResolvers() {
		return (this.argumentResolvers != null ? this.argumentResolvers.getResolvers() : null);
	}
	
	//可以通过此方法加入自定义的参数解析器,所有参数解析器都应该实现 HandlerMethodArgumentResolver 接口
	public void setCustomArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {
		this.customArgumentResolvers = argumentResolvers;
	}
	
	//可以通过此方法加入自定义的返回值处理器,所有返回值处理器都应该实现 HandlerMethodReturnValueHandler 接口
	public void setCustomReturnValueHandlers(@Nullable List<HandlerMethodReturnValueHandler> returnValueHandlers) {
		this.customReturnValueHandlers = returnValueHandlers;
	}
}

实现参数解析器接口

public interface HandlerMethodArgumentResolver {
	//只有此方法返回值为true,解析器才会继续调用下面的 resolveArgument 方法正式解析参数
	boolean supportsParameter(MethodParameter parameter);

	//编写解析的逻辑,并把解析后得到的参数值返回
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

实现返回值处理器接口

public interface HandlerMethodReturnValueHandler {
	//判断是否需要处理返回值
	boolean supportsReturnType(MethodParameter returnType);

	//编写处理返回值的具体逻辑,可以通过webRequest拿到原始的请求,响应对象,再通过响应对象将返回值写入输出流
	//webRequest.getNativeResponse(HttpServletResponse.class)
	//默认情况下,即使已经通过这里将返回值写入了输出流,SpringMVC仍然会进行视图解析的步骤,可以通过调用 mavContainer.setRequestHandled(true) 告知请求已经处理完毕
	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

关于@ControllerAdvice

控制器增强(和AOP无关),可以配合以下三种注解使用

  1. @ExceptionHandler:统一异常处理
  2. @ModelAttribute:被此注解标注的方法,其返回值会作为模型数据补充到控制器的执行过程中
  3. @InitBinder:自定义类型转换器,其来源有两个
    1. @ControllerAdvice 中 @InitBinder 标注的方法,由 RequestMappingHandlerAdapter 在初始化时解析并记录
    2. @Controller 中 @InitBinder 标注的方法,由 RequestMappingHandlerAdapter 在控制器方法首次执行时解析并记录

控制器方法执行流程

核心是:ServletInvocableHandlerMethod,需要以下几个组件共同参与

1、数据绑定与类型转换 WebDataBinderFactory

2、解析参数名称 ParameterNameDiscoverer

3、参数解析器 HandlerMethodArgumentResolverComposite

@RequestParam("name")
@RequestParam(name = "xxx", defaultValue = "${JAVA_HOME}"):可以获取Spring提供的参数
@PathVariable("id")
@RequestHeader("Content-Type")
@CookieValue
@Value("${JAVA_HOME}")
@ModelAttribute User user:可以省略@ModelAttribute
@RequestBody

4、返回值处理器 HandlerMethodReturnValueHandlerComposite

@ControllerAdvice 配合 ResponseBodyAdvice接口,对响应做增强

@ControllerAdvice
public class MyControllerAdvice implements ResponseBodyAdvice<Object> {
    //判断是否满足条件
	@Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return false;
    }

	//Object body: 接口的原始响应体
	//MethodParameter returnType:接口的方法信息
	//return 包装后的响应体
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return null;
    }
}
posted @ 2023-06-14 09:30  谭五月  阅读(12)  评论(0)    收藏  举报