Spring5底层原理 学习笔记(三)WEB篇

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中

获取参数名

  1. 如果编译时添加了 -parameters 可以生成参数表, 反射时就可以拿到参数名
  2. 如果编译时添加了 -g 可以生成调试信息, 但分为两种情况
    • 普通类, 会包含局部变量表, 用 asm 可以拿到参数名
    • 接口, 不会包含局部变量表, 无法获得参数名
      • 这也是 MyBatis 在实现 Mapper 接口时为何要提供 @Param 注解来辅助获得参数名

对象绑定与类型转换

底层第一套转换接口与实现

«interface»
Formatter
«interface»
Printer
«interface»
Parser
Converters
Set<GenericConverter>
«interface»
Converter
«interface»
ConversionService
FormattingConversionService
Adapter1
Adapter2
Adapter3
  • Printer 把其它类型转为 String
  • Parser 把 String 转为其它类型
  • Formatter 综合 Printer 与 Parser 功能
  • Converter 把类型 S 转为类型 T
  • Printer、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合
  • FormattingConversionService 利用其它们实现转换

底层第二套转换接口

«interface»
PropertyEditorRegistry
«interface»
PropertyEditor
  • PropertyEditor 把 String 与其它类型相互转换
  • PropertyEditorRegistry 可以注册多个 PropertyEditor 对象
  • 与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配

高层接口与实现

«interface»
TypeConverter
SimpleTypeConverter
BeanWrapperImpl
DirectFieldAccessor
ServletRequestDataBinder
TypeConverterDelegate
«interface»
ConversionService
«interface»
PropertyEditorRegistry
  • 它们都实现了 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 +
                    '}';
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值