RequestMappingHandlerMapping和RequestMappingHandlerAdaptor
DispatcherServlet初始化
准备一个ApplicationContext和一个配置类
@Configuration
@ComponentScan
public class WebConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
}
public class Demo {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
}
执行main方法,可以在控制台看到启动成功
DispatcherServlet的初始化时机
启动的时候并没有被初始化
通过浏览器访问,观察控制台发现这时DispatcherServlet才被初始化
可以通过设置loadOnStartUp=1来实现程序启动时就初始化DispatcherServlet
通过读取配置文件的方式,配合EnableProperties,动态读取配置文件内容
application.properties
server.port=9090
spring.mvc.servlet.load-on-startup=1
WebConfig
再次启动项目
初始化执行的操作
这些方法都是判断是否提供了对应的组件,如果使用者没有提供,那么会到“DispatcherServlet.properties”文件中加载默认的类
RequestMappingHandlerMapping
RequestMapping的处理器映射器,用于映射请求路径与处理方法
由于Spring容器启动的时候不会把RequestMappingHandlerMapping添加到容器中进行管理,所以我们手动在配置类中把RequestMappingHandlerMapping添加到容器中
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
准备一个controller
@RequestMapping
@Controller
public class Controller1 {
@GetMapping("get")
public void get() {
System.out.println("get---------");
}
@PostMapping("save")
public void save() {
}
}
启动容器
handlerMapping存储了所有的映射结果,通过getHandlerMethods得到映射结果并且打印
public class Demo {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
handlerMethods.forEach((info, method) -> System.out.println(info + "=======" + method));
}
}
控制台打印
请求来了,通过getHandler方法返回一个执行链对象,执行链对象包含了handler方法和拦截器
为使用Mock,导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.20</version>
</dependency>
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
// handlerMethods.forEach((info, method) -> System.out.println(info + "=======" + method));
HandlerExecutionChain chain = requestMappingHandlerMapping.getHandler(new MockHttpServletRequest("GET", "/get"));
System.out.println(chain);
}
RequestMappingHandlerAdapter
这个类是执行HandlerMethod的
执行方法是invokeHandlerMethod
但是这个方法是受保护的,为了测试能够使用到,我们定义一个它的子类MyRequestMappingHandlerAdapter 不改动逻辑只把这个方法改成public,并通过@Bean注入到容器中
public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
@Override
public ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
return super.invokeHandlerMethod(request, response, handlerMethod);
}
}
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
return new MyRequestMappingHandlerAdapter();
}
测试类手动调用
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/get");
HandlerExecutionChain chain = requestMappingHandlerMapping.getHandler(request);
System.out.println(chain);
MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
handlerAdapter.invokeHandlerMethod(request, new MockHttpServletResponse(), (HandlerMethod) chain.getHandler());
}
参数解析器和返回值解析器
一个请求的参数和返回值都需要解析器进行解析,这些解析器都在RequestMappingHandlerAdapter里
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
System.out.println("========================所有的参数解析器========================");
Objects.requireNonNull(handlerAdapter.getArgumentResolvers()).forEach(System.out::println);
System.out.println("========================所有的返回值解析器========================");
Objects.requireNonNull(handlerAdapter.getReturnValueHandlers()).forEach(System.out::println);
}
控制台输出
========================所有的参数解析器========================
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@841e575
org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver@27a5328c
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver@1e5f4170
org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver@6c345c5f
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver@6b5966e1
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver@65e61854
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@1568159
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@4fcee388
org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver@6f80fafe
org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver@3af17be2
org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver@f9879ac
org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver@37f21974
org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver@5f4d427e
org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver@6e521c1e
org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver@224b4d61
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver@5d5d9e5
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver@303e3593
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@4ef27d66
org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver@362a019c
org.springframework.web.method.annotation.ModelMethodProcessor@1d9bec4d
org.springframework.web.method.annotation.MapMethodProcessor@5c48c0c0
org.springframework.web.method.annotation.ErrorsMethodArgumentResolver@10c8f62
org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver@674c583e
org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver@25f7391e
org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver@3f23a3a0
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@5ab14cb9
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@5fb97279
========================所有的返回值解析器========================
org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@31024624
org.springframework.web.method.annotation.ModelMethodProcessor@25bcd0c7
org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@32cb636e
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler@63cd604c
org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler@40dd3977
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@3a4e343
org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler@6a1d204a
org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@62dae245
org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@4b6579e8
org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler@6fff253c
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@6c6357f9
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@591e58fa
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler@3954d008
org.springframework.web.method.annotation.MapMethodProcessor@2f94c4db
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@593e824f
自定义参数解析器
创建一个自定义注解@Token
@Target(ElementType.PARAMETER) //加在方法的参数上
@Retention(RetentionPolicy.RUNTIME) //注解在运行期都有效
public @interface Token {
}
在Controller中添加一个方法
@PutMapping("test")
public void test(@Token String token) {
System.out.println("receive a token : " + token);
}
创建一个HandlerMethodArgumentResolver的实现类,实现两个方法,supportsParameter的返回值表示是否在这个参数上生效,resolveArgument就是具体的解析过程,将返回值赋值给当前参数
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(Token.class) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return webRequest.getHeader("token");
}
}
添加到RequestMappingHandlerAdapter中
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter() {
MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
List<HandlerMethodArgumentResolver> methodArgumentResolverArrayList = new ArrayList<>(1);
methodArgumentResolverArrayList.add(new TokenArgumentResolver());
handlerAdapter.setCustomArgumentResolvers(methodArgumentResolverArrayList);
return handlerAdapter;
}
测试
public class Demo2 {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test");
request.addHeader("token", "SDKLJ98asjdf9smdnfaSDF7");
HandlerExecutionChain chain = requestMappingHandlerMapping.getHandler(request);
MyRequestMappingHandlerAdapter handlerAdapter = context.getBean(MyRequestMappingHandlerAdapter.class);
handlerAdapter.invokeHandlerMethod(request, new MockHttpServletResponse(), (HandlerMethod) chain.getHandler());
}
}
自定义返回值解析器
思路跟自定义参数解析器类似
自定义@Yml注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Yml {
}
自定义加了@Yml注解的返回值处理器YmlReturnValueHandler,实现HandlerMethodReturnValueHandler接口
public class YmlReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Yml yml = returnType.getMethodAnnotation(Yml.class);
return yml != null;
}
@Override //returnValue:返回值 webRequest:既有原始的请求,也有原始的响应
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//1.转换返回结果为 yaml 字符串
String str = new Yaml().dump(returnValue);
//2.将yaml字符串写入响应
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
response.setContentType("text/plain;charset=utf-8");
response.getWriter().print(str);
//3.设置请求已经处理完毕
mavContainer.setRequestHandled(true);
}
}
在WebConfig配置类的MyRequestMappingHandlerAdapter中设置自定义返回值解析器
@Bean
public MyRequestMappingHandlerAdapter requestMappingHandlerAdapter(){
TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler();
MyRequestMappingHandlerAdapter handlerAdapter = new MyRequestMappingHandlerAdapter();
//设置自定义参数解析器
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
argumentResolvers.add(tokenArgumentResolver);
handlerAdapter.setCustomArgumentResolvers(argumentResolvers);
//设置自定义返回值处理器
List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
returnValueHandlers.add(ymlReturnValueHandler);
handlerAdapter.setCustomReturnValueHandlers(returnValueHandlers);
return handlerAdapter;
}
参数解析器
准备
定义一个控制器类ControllerUser,在其中测试常见参数的解析,同时定义测试需要的实体类User,具体实现如下:
static class Controller {
public void test(@RequestParam("name1") String name1,
String name2,
@RequestParam("age") int age,
@RequestParam(name = "home", defaultValue = "${JAVA_HOME}") String home1,
@RequestParam("file") MultipartFile file,
@PathVariable("id") int id,
@RequestHeader("Content-Type") String header,
@CookieValue("token") String token,
@Value("${JAVA_HOME}") String home2,
HttpServletRequest request,
@ModelAttribute User user1,
User user2,
@RequestBody User user3
) {
}
}
@Data
static class User {
private String name;
private int age;
}
定义一个模拟浏览器发送请求的方法mockRequest(),方便进行测试,具体实现如下:
private static HttpServletRequest mockRequest() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name1", "zhangsan");
request.setParameter("name2", "lisi");
request.addPart(new MockPart("file", "abc", "hello".getBytes(StandardCharsets.UTF_8)));
Map<String, String> map = new AntPathMatcher()
.extractUriTemplateVariables("/test/{id}", "/test/123");
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
request.setContentType("application/json");
request.setCookies(new Cookie("token", "123456"));
request.setParameter("name", "zhangsan");
request.setParameter("age", "18");
request.setContent("""
{
"name":"李四",
"age":20
}"""
.getBytes(StandardCharsets.UTF_8));
return new StandardServletMultipartResolver().resolveMultipart(request);
}
测试并打印所有参数的相关信息
public static void main(String[] args) throws NoSuchMethodException {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
HttpServletRequest request = mockRequest();
// 1. 创建HandlerMethod对象
HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
// 2. 准备对象绑定与类型转换
// 3. 准备ModelAndViewContainer用来存储中间Model的结果
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();
// 4. 解析每个参数值
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
Annotation[] parameterAnnotations = methodParameter.getParameterAnnotations();
String collect = Arrays.stream(parameterAnnotations).map(annotation -> annotation.annotationType().getSimpleName()).collect(Collectors.joining());
collect = collect.length() > 0 ? " @" + collect : " ";
methodParameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
String text = "[" + methodParameter.getParameterIndex() + "]" + collect + " " + methodParameter.getParameterType().getSimpleName()
+ " " + methodParameter.getParameterName();
System.out.println(text);
}
}
RequestParamArgumentResolver
测试@RequestParam注解
{
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
HttpServletRequest request = mockRequest();
// 1. 创建HandlerMethod对象
HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
// 2. 准备对象绑定与类型转换
ServletRequestDataBinderFactory dataBinderFactory = new ServletRequestDataBinderFactory(null, null);
// 3. 准备ModelAndViewContainer用来存储中间Model的结果
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();
// 4. 解析每个参数值
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
// useDefaultResolution = false,表示 @RequestParam 注解是非必须的
RequestParamMethodArgumentResolver requestParamMethodArgumentResolver = new RequestParamMethodArgumentResolver(beanFactory, false);
Annotation[] parameterAnnotations = methodParameter.getParameterAnnotations();
String collect = Arrays.stream(parameterAnnotations).map(annotation -> annotation.annotationType().getSimpleName()).collect(Collectors.joining());
collect = collect.length() > 0 ? " @" + collect : " ";
// 名字查找器
methodParameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
String text = "[" + methodParameter.getParameterIndex() + "]" + collect + " " + methodParameter.getParameterType().getSimpleName()
+ " " + methodParameter.getParameterName();
if (requestParamMethodArgumentResolver.supportsParameter(methodParameter)) {
Object paramValue = requestParamMethodArgumentResolver.resolveArgument(methodParameter, modelAndViewContainer,
new ServletWebRequest(request), dataBinderFactory);
System.out.println(text + "->" + paramValue.getClass().getSimpleName() + " " + paramValue);
} else {
System.out.println(text);
}
}
}
参数解析器-组合模式
Spring提供了一个类叫做HandlerMethodArgumentResolverComposite,用于处理多个参数解析器标注的参数
测试HandlerMethodArgumentResolverComposite
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
HttpServletRequest request = mockRequest();
// 1. 创建HandlerMethod对象
HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
// 2. 准备对象绑定与类型转换
ServletRequestDataBinderFactory dataBinderFactory = new ServletRequestDataBinderFactory(null, null);
// 3. 准备ModelAndViewContainer用来存储中间Model的结果
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();
// 4. 解析每个参数值
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
HandlerMethodArgumentResolverComposite handlerMethodArgumentResolverComposite = new HandlerMethodArgumentResolverComposite();
// useDefaultResolution = false,表示 @RequestParam 注解是非必须的
handlerMethodArgumentResolverComposite.addResolver(new RequestParamMethodArgumentResolver(beanFactory, false));
Annotation[] parameterAnnotations = methodParameter.getParameterAnnotations();
String collect = Arrays.stream(parameterAnnotations).map(annotation -> annotation.annotationType().getSimpleName()).collect(Collectors.joining());
collect = collect.length() > 0 ? " @" + collect : " ";
// 名字查找器
methodParameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
String text = "[" + methodParameter.getParameterIndex() + "]" + collect + " " + methodParameter.getParameterType().getSimpleName()
+ " " + methodParameter.getParameterName();
if (handlerMethodArgumentResolverComposite.supportsParameter(methodParameter)) {
Object paramValue = handlerMethodArgumentResolverComposite.resolveArgument(methodParameter, modelAndViewContainer,
new ServletWebRequest(request), dataBinderFactory);
System.out.println(text + "->" + paramValue.getClass().getSimpleName() + " " + paramValue);
} else {
System.out.println(text);
}
}
}
其他参数解析器
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
HttpServletRequest request = mockRequest();
// 1. 创建HandlerMethod对象
HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
// 2. 准备对象绑定与类型转换
ServletRequestDataBinderFactory dataBinderFactory = new ServletRequestDataBinderFactory(null, null);
// 3. 准备ModelAndViewContainer用来存储中间Model的结果
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();
// 4. 解析每个参数值
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
HandlerMethodArgumentResolverComposite handlerMethodArgumentResolverComposite = new HandlerMethodArgumentResolverComposite();
handlerMethodArgumentResolverComposite.addResolvers(
// 解析@RequestParam ,useDefaultResolution = false,表示 @RequestParam 注解是非必须的
new RequestParamMethodArgumentResolver(beanFactory, false),
// 解析@PathVariable
new PathVariableMethodArgumentResolver(),
// 解析@RequestHeader
new RequestHeaderMethodArgumentResolver(beanFactory),
// 解析@CookieValue
new ServletCookieValueMethodArgumentResolver(beanFactory),
// 解析@Value
new ExpressionValueMethodArgumentResolver(beanFactory),
// 根据类型解析
new ServletRequestMethodArgumentResolver(),
// 解析@ModelAndView, false表示需要有注解
new ServletModelAttributeMethodProcessor(false),
// 解析@RequestBody
new RequestResponseBodyMethodProcessor(Lists.newArrayList(new MappingJackson2HttpMessageConverter())),
// 解析模型数据,不带@ModelAndView,用于兜底
new ServletModelAttributeMethodProcessor(true),
// 解析请求参数,不带@RequestParam,用于兜底.useDefaultResolution = true,表示 @RequestParam 注解是必须的
new RequestParamMethodArgumentResolver(beanFactory, true)
);
Annotation[] parameterAnnotations = methodParameter.getParameterAnnotations();
String collect = Arrays.stream(parameterAnnotations).map(annotation -> annotation.annotationType().getSimpleName()).collect(Collectors.joining());
collect = collect.length() > 0 ? " @" + collect : " ";
// 名字查找器
methodParameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
String text = "[" + methodParameter.getParameterIndex() + "]" + collect + " " + methodParameter.getParameterType().getSimpleName()
+ " " + methodParameter.getParameterName();
if (handlerMethodArgumentResolverComposite.supportsParameter(methodParameter)) {
Object paramValue = handlerMethodArgumentResolverComposite.resolveArgument(methodParameter, modelAndViewContainer,
new ServletWebRequest(request), dataBinderFactory);
System.out.println("parsed : " + text + "->" + paramValue.getClass().getSimpleName() + " " + paramValue);
} else {
System.out.println("unparsed : " + text);
}
}
System.out.println(modelAndViewContainer);
}
其中@PathVariable会将路径占位符的变量名和对应的值封装到一个map中
Map<String, String> map = new AntPathMatcher()
.extractUriTemplateVariables("/test/{id}", "/test/123");
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
map中会添加一个键值对 id -> 123
并且实体类会被保存到ModelAndViewContainer中
获取参数名
- 如果编译时添加了 -parameters 可以生成参数表, 反射时就可以拿到参数名
- 如果编译时添加了 -g 可以生成调试信息, 但分为两种情况
- 普通类, 会包含局部变量表, 用 asm 可以拿到参数名
- 接口, 不会包含局部变量表, 无法获得参数名
- 这也是 MyBatis 在实现 Mapper 接口时为何要提供 @Param 注解来辅助获得参数名
对象绑定与类型转换
底层第一套转换接口与实现
- Printer 把其它类型转为 String
- Parser 把 String 转为其它类型
- Formatter 综合 Printer 与 Parser 功能
- Converter 把类型 S 转为类型 T
- Printer、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合
- FormattingConversionService 利用其它们实现转换
底层第二套转换接口
- PropertyEditor 把 String 与其它类型相互转换
- PropertyEditorRegistry 可以注册多个 PropertyEditor 对象
- 与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配
高层接口与实现
- 它们都实现了 TypeConverter 这个高层转换接口,在转换时,会用到 TypeConverter Delegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换(Facade 门面模式)
- 首先看是否有自定义转换器, @InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor)
- 再看有没有 ConversionService 转换
- 再利用默认的 PropertyEditor 转换
- 最后有一些特殊处理
- SimpleTypeConverter 仅做类型转换
- BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,走 Property
- DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,走 Field
- ServletRequestDataBinder 为 bean 的属性执行绑定,当需要时做类型转换,根据 directFieldAccess 选择走 Property 还是 Field,具备校验与获取校验结果功能
基本的类型转换与数据绑定用法
- SimpleTypeConverter
public class TestSimpleConverter {
public static void main(String[] args) {
//仅有类型转换功能
SimpleTypeConverter converter = new SimpleTypeConverter();
Integer number = converter.convertIfNecessary("13", int.class);
Date date = converter.convertIfNecessary("1999/03/04", Date.class);
System.out.println(number);
System.out.println(date);
}
}
- BeanWrapperImpl
public class TestBeanWrapper {
public static void main(String[] args) {
//利用反射原理,为bean的属性赋值
MyBean target = new MyBean();
BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
wrapper.setPropertyValue("a","10");
wrapper.setPropertyValue("b","hello");
wrapper.setPropertyValue("c","1999/03/04");
System.out.println(target);
}
static class MyBean{
private int a;
private String b;
private Date c;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public Date getC() {
return c;
}
public void setC(Date c) {
this.c = c;
}
@Override
public String toString() {
return "MyBean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}
- DirectFieldAccessor
public class TestFieldAccessor {
public static void main(String[] args) {
//利用反射原理,为bean的属性赋值
MyBean target = new MyBean();
DirectFieldAccessor accessor = new DirectFieldAccessor(target);
accessor.setPropertyValue("a","10");
accessor.setPropertyValue("b","hello");
accessor.setPropertyValue("c","1999/03/04");
System.out.println(target);
}
static class MyBean{
private int a;
private String b;
private Date c;
@Override
public String toString() {
return "MyBean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}
- ServletRequestDataBinder
public class TestDataBinder {
public static void main(String[] args) {
//执行数据绑定
MyBean target = new MyBean();
DataBinder dataBinder = new DataBinder(target);
dataBinder.initDirectFieldAccess();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("a","10");
pvs.add("b","hello");
pvs.add("c","1999/03/04");
dataBinder.bind(pvs);
System.out.println(target);
}
static class MyBean{
private int a;
private String b;
private Date c;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public Date getC() {
return c;
}
public void setC(Date c) {
this.c = c;
}
@Override
public String toString() {
return "MyBean{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}