0
点赞
收藏
分享

微信扫一扫

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)


文章目录

  • ​​0.前记​​
  • ​​1.RequestMappingHandlerAdapter 的初始化​​
  • ​​2.参数解析器解析​​
  • ​​3.自定义参数解析器​​
  • ​​4.参数解析器的执行过程​​
  • ​​5.参数解析过程​​

0.前记

根据之前的文章可知​​HandlerAdapter​​​主要的是​​RequestMappingHandlerAdapter​​的效果

本章解释一下​​RequestMappingHandlerAdapte​​r的具体使用情况

1.RequestMappingHandlerAdapter 的初始化

根据前文可知在​​DispatcherServlet​​​初始化的时候会初始化​​HandlerAdapter​

这里默认会初始化​​RequestMappingHandlerAdapter,​​​ 而​​RequestMappingHandlerAdapter​​​ 实现了​​InitializingBean​​接口

会实现​​afterPropertiesSet()​​方法

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_tomcat

  1. 初始化@ControllerAdvice
  2. 初始化参数解析器 argumentResolvers
  3. 初始化绑定参数解析器 initBinderArgumentResolvers
  4. 初始化返回值解析器 returnValueHandlers

下面列举的都是参数解析器

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_servlet_02


以上就添加了参数解析器和返回值解析器

2.参数解析器解析

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_java_03


WebConfig.java

@Configuration
@ComponentScan
@PropertySource("classpath:application.properties")
@EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class})
public class WebConfig {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties){
return new TomcatServletWebServerFactory(serverProperties.getPort());
}

// DispatcherServlet初始化时机: 创建是Spring创建的, 初始化为tomcat初始化的: 第一次请求的时候初始化, 可以设置为tomcat服务器启动就初始化好
// 初始化: onRefresh()
// RequestMappingHandlerMapping: 解析@RequestMapping 以及派生注解, 生成路径和控制器方法的映射关系,
// RequestMappingHandlerAdaptor: 调用控制器方法
@Bean
public DispatcherServlet dispatcherServlet(){
return new DispatcherServlet();
}

@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(WebMvcProperties webMvcProperties){
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet(), "/");
// tomcat启动的时候DS初始化完毕
registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
return registrationBean;
}

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(){
return new RequestMappingHandlerMapping();
}

@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter(){
return new MyRequestMappingHandlerAdapter();
}

}

MyRequestMappingHandlerAdapter.java

public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
@Override
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
return super.invokeHandlerMethod(request, response, handlerMethod);
}
}

Controller1.java

@RestController
public class Controller1 {
@GetMapping("/test1")
public String test1(){
System.out.println("1");
return "1";
}
}

A20.java

public class A20 {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);

RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 路径信息, 控制器方法
Map<RequestMappingInfo, HandlerMethod> map = handlerMapping.getHandlerMethods();
map.forEach((k, v) -> {
System.out.println(k + " = " + v);
});

// 返回处理器执行链对象
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test1");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);

MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());

System.out.println(">>>>>>>> 参数解析器");
for (HandlerMethodArgumentResolver argumentResolver : handlerAdapter.getArgumentResolvers()) {
System.out.println(argumentResolver);
}

System.out.println(">>>>>>>> 返回值解析器");
for (HandlerMethodReturnValueHandler returnValueHandler : handlerAdapter.getReturnValueHandlers()) {
System.out.println(returnValueHandler);
}

}
}

获取到参数解析器和返回值解析器

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_tomcat_04


SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_java_05

模拟发生get请求

MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
handlerAdapter.invokeHandlerMethod(request, response, (HandlerMethod) chain.getHandler());

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_初始化_06

3.自定义参数解析器

这里自定义了一个@Token, 用于解析参数的带有头信息的token值

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_tomcat_07

  1. Token .java

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
}

  1. TokenArgumentResolver.java

public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
// 是否支持某个参数
@Override
public boolean supportsParameter(MethodParameter parameter) {
Token token = parameter.getParameterAnnotation(Token.class);

return token != null;
}

// 解析参数
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return webRequest.getHeader("token");
}
}

  1. WebConfig.java

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_解析器_08

  1. Controller1.java

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_tomcat_09

  1. A20.java

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_java_10

  1. 这里利用了模拟PUT请求/test2的, 并加入了token

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_java_11

4.参数解析器的执行过程

先拿到方法的参数, 然后解析, 最后通过反射执行

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_解析器_12

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_解析器_13

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_tomcat_14

所以说最后是通过反射去执行方法的。

5.参数解析过程

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_初始化_15

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

  1. 先找到提供的参数​​findProvidedArgument​
  2. 解析器进行解析​​resolveArgument​​​, 之前先判断解析器是否可以解析​​supportsParameter​

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

这里先看缓存有无, 第一次的话缓存肯定无, 那么就加入到缓存。这里为优化部分

下面的为实现了​​supportsParameter​​ 方法的参数解析器

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (九)_解析器_16


举报

相关推荐

0 条评论