02-参数解析器

上一节通过调用方法获取了Spring提供的参数解析器,这节通过案例测试这些参数解析器的工作方式。

MyRequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(MyRequestMappingHandlerAdapter.class);
handlerAdapter.getArgumentResolvers().forEach(System.out::println);

准备工作

准备配置以及Model

@Configuration
class WebConfig {
}

@Data
class User {
    private String name;
    private int age;
}

准备一个模拟控制器

分别测试

  • @RequestParam参数
  • 不带@RequestParam参数
  • @RequestParam int类型
  • @RequestParam中默认值是Spring环境参数
  • @RequestParam 文件类型的
  • @PathVariable
  • @RequestHeader
  • @CookieValue
  • @Value
  • HttpServletRequest
  • @ModelAttribute
  • 不带@ModelAttribute参数
  • @RequestBody
class TestController {
    public void test(
            @RequestParam("name1") String name1,   // name1=张三
            String name2,                           // name2=李四       不带注解@RequestParam
            @RequestParam("age") int age,           // age=18    默认字符串  需要处理类型转换
            @RequestParam(name = "home", defaultValue = "${JAVA_HOME}") String home1,   // Spring环境获取数据
            @RequestParam("file") MultipartFile file,    //文件上传
            @PathVariable("id") int id,                // 请求路径参数    /test/124    test/{id}
            @RequestHeader("Content-Type") String header,   // 请求头参数
            @CookieValue("token") String token,      // cookie获取数据
            @Value("${JAVA_HOME}") String home2,     // Spring环境获取数据
            HttpServletRequest request,           // request,response,session
            @ModelAttribute("abc") User user1,   // name=张三&age=18
            User user2,                           // name=张三&age=18  省略@ModelAttribute
            @RequestBody User user3         //   json
    ) {
    }
}

准备一个模拟请求

private static HttpServletRequest mockRequest() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    // @RequestParam
    request.setParameter("name1", "张三");
    request.setParameter("name2", "李四");
    request.setParameter("age", "18");
    // 文件
    request.addPart(new MockPart("file", "abc", "hello world".getBytes(StandardCharsets.UTF_8)));
    // @PathVariable  解析路径参数并放入RequestAttribute中
    Map<String, String> uriTemplateVariables = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/124");
    request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
    // @RequestHeader
    request.setContentType("application/json");
    // @CookieValue
    request.setCookies(new Cookie("token", "2345"));
    // @ModelAttribute
    request.setParameter("name", "张三");
    // @RequestBody
    request.setContent("{\"name\": \"李四\", \"age\":\"20\"}".getBytes(StandardCharsets.UTF_8));

    return new StandardServletMultipartResolver().resolveMultipart(request);
}

RequestParamMethodArgumentResolver

// 0. 准备Spring环境    非Web环境
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(WebConfig.class);
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();

// 1. 准备请求
HttpServletRequest request = mockRequest();
// 2. 准备控制器方法
HandlerMethod handlerMethod = new HandlerMethod(new TestController(), TestController.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));

// 3. 准备绑定对象的类型转换  处理数据类型转换  例如 字符串->数字
ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(null, null);

// 4. 准备 ModelAndViewContainer容器存储中间Model对象
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();

// RequestParam参数解析器       false表示参数上必须有注解才会解析
RequestParamMethodArgumentResolver requestParamMethodArgumentResolver = new RequestParamMethodArgumentResolver(beanFactory, false);

// 5. 解析每个参数的值
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
    // 获取这个参数的注解
    String annotationVal = Arrays.stream(methodParameter.getParameterAnnotations()).map(annotation -> annotation.annotationType().getSimpleName()).collect(Collectors.joining());
    String paramAnnotation = StrUtil.isNotBlank(annotationVal) ? "@" + annotationVal + " " : " ";
    // 参数名生成
    methodParameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
    StringBuilder ret = new StringBuilder("[").append(methodParameter.getParameterIndex()).append("] ").append(paramAnnotation)
            .append(methodParameter.getParameterType().getSimpleName()).append(" ").append(methodParameter.getParameterName());
    if (requestParamMethodArgumentResolver.supportsParameter(methodParameter)) {
        Object paramValue = requestParamMethodArgumentResolver.resolveArgument(methodParameter, modelAndViewContainer, new ServletWebRequest(request), servletRequestDataBinderFactory);
        // System.out.println("解析的参数类型" + paramValue.getClass());
        ret.append("->").append(paramValue);
    }
    System.out.println(ret);
}

组合模式

如果对每个参数进行所有解析器逐个supportsParameter判断解析,代码会很啰嗦,Spring中使用组合模式进行参数解析.

// 0. 准备Spring环境
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(WebConfig.class);
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();

// 1. 准备请求
HttpServletRequest request = mockRequest();
// 2. 准备控制器方法
HandlerMethod handlerMethod = new HandlerMethod(new TestController(), TestController.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));

// 3. 准备绑定对象的类型转换  处理数据类型转换  例如 字符串->数字
ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(null, null);

// 4. 准备 ModelAndViewContainer容器存储中间Model对象
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();

// 组合模式
HandlerMethodArgumentResolverComposite resolverComposite = new HandlerMethodArgumentResolverComposite();
resolverComposite.addResolvers(
        // RequestParam参数解析器       false表示参数上必须有注解才会解析
        new RequestParamMethodArgumentResolver(beanFactory, false)
);

// 5. 解析每个参数的值
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
    // 获取这个参数的注解
    String annotationVal = Arrays.stream(methodParameter.getParameterAnnotations()).map(annotation -> annotation.annotationType().getSimpleName()).collect(Collectors.joining());
    String paramAnnotation = StrUtil.isNotBlank(annotationVal) ? "@" + annotationVal + " " : " ";
    // 参数名生成
    methodParameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
    StringBuilder ret = new StringBuilder("[").append(methodParameter.getParameterIndex()).append("] ").append(paramAnnotation)
            .append(methodParameter.getParameterType().getSimpleName()).append(" ").append(methodParameter.getParameterName());
    if (resolverComposite.supportsParameter(methodParameter)) {
        Object paramValue = resolverComposite.resolveArgument(methodParameter, modelAndViewContainer, new ServletWebRequest(request), servletRequestDataBinderFactory);
        // System.out.println("解析的参数类型" + paramValue.getClass());
        ret.append("->").append(paramValue);
    }
    System.out.println(ret);
}

其余参数解析器

// 0. 准备Spring环境
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(WebConfig.class);
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();

// 1. 准备请求
HttpServletRequest request = mockRequest();
// 2. 准备控制器方法
HandlerMethod handlerMethod = new HandlerMethod(new TestController(), TestController.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));

// 3. 准备绑定对象的类型转换  处理数据类型转换  例如 字符串->数字
ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(null, null);

// 4. 准备 ModelAndViewContainer容器存储中间Model对象
ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer();

// 组合模式
HandlerMethodArgumentResolverComposite resolverComposite = new HandlerMethodArgumentResolverComposite();
resolverComposite.addResolvers(
        // @RequestParam参数解析器       false表示参数上必须有注解才会解析
        new RequestParamMethodArgumentResolver(beanFactory, false),
        // @PathVariable
        new PathVariableMethodArgumentResolver(),
        // @RequestHeader
        new RequestHeaderMethodArgumentResolver(beanFactory),
        // @CookieValue
        new ServletCookieValueMethodArgumentResolver(beanFactory),
        // Spring表达式参数解析器
        new ExpressionValueMethodArgumentResolver(beanFactory),
        // HttpServletRequest
        new ServletRequestMethodArgumentResolver(),
        // 是否不需要注解@ModelAttribute false:需要  即没有@ModelAttribute的实体参数不解析
        new ServletModelAttributeMethodProcessor(false),
        // @RequestBody
        new RequestResponseBodyMethodProcessor(CollUtil.newArrayList(new MappingJackson2HttpMessageConverter())),
        /**
         *是否不需要注解@ModelAttribute true:不需要  即没有@ModelAttribute的实体参数也解析
         * 注意!!!  这个解析器必须放在@RequestBody解析器后面,否则这个解析器将会优先被使用去解析
         */
        new ServletModelAttributeMethodProcessor(true),
        /**
         * @RequestParam 参数解析器 true表示参数上没有注解也会解析
         * 注意!!!  这个解析器必须放在后面 否则其它类型参数将使用这个解析器解析
         */
        new RequestParamMethodArgumentResolver(beanFactory, true)

);

// 5. 解析每个参数的值
for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
    // 获取这个参数的注解
    String annotationVal = Arrays.stream(methodParameter.getParameterAnnotations()).map(annotation -> annotation.annotationType().getSimpleName()).collect(Collectors.joining());
    String paramAnnotation = StrUtil.isNotBlank(annotationVal) ? "@" + annotationVal + " " : " ";
    // 参数名生成
    methodParameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
    StringBuilder ret = new StringBuilder("[").append(methodParameter.getParameterIndex()).append("] ").append(paramAnnotation)
            .append(methodParameter.getParameterType().getSimpleName()).append(" ").append(methodParameter.getParameterName());
    if (resolverComposite.supportsParameter(methodParameter)) {
        Object paramValue = resolverComposite.resolveArgument(methodParameter, modelAndViewContainer, new ServletWebRequest(request), servletRequestDataBinderFactory);
        // System.out.println("解析的参数类型" + paramValue.getClass());
        ret.append("->").append(paramValue);
    }
    System.out.println(ret);
    System.out.println("模型数据为:" + modelAndViewContainer.getModel());
}

类型转换器

准备Bean

准备一个带Getter,Setter的Bean,和一个不带Getter,Setter的Bean

@Getter
@Setter
@ToString
class Bean1 {
    private int a;
    private String b;
    private Date c;
}

@ToString
class Bean2 {
    private int a;
    private String b;
    private Date c;
}

SimpleTypeConverter

private static void testSimpleTypeConverter() {
    // 仅有类型转换功能
    SimpleTypeConverter simpleTypeConverter = new SimpleTypeConverter();
    Integer num = simpleTypeConverter.convertIfNecessary("12", int.class);
    Date date = simpleTypeConverter.convertIfNecessary("2023/03/19", Date.class);
    System.out.println("num = " + num);
    System.out.println("date = " + date);
}

BeanWrapperImpl

private static void testBeanWrapper() {
    Bean1 target = new Bean1();
    // 通过反射调用Bean的get set方法  所操作的属性必须要有set方法
    BeanWrapperImpl beanWrapper = new BeanWrapperImpl(target);
    beanWrapper.setPropertyValue("a", "123");
    beanWrapper.setPropertyValue("b", "456");
    beanWrapper.setPropertyValue("c", "2023/03/19");
    System.out.println("target = " + target);
}

DirectFieldAccessor

private static void testDirectFieldAccessor() {
    Bean2 target = new Bean2();
    // 通过反射调用  直接操作bean的属性
    DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(target);
    directFieldAccessor.setPropertyValue("a", "123");
    directFieldAccessor.setPropertyValue("b", "456");
    directFieldAccessor.setPropertyValue("c", "2023/03/19");
    System.out.println("target = " + target);
}

数据绑定器

DataBinder+BeanWrapperImpl

private static void testDataBinder() {
    Bean1 target = new Bean1();
    // 默认走反射调用set方法的方式  即BeanWrapperImpl
    DataBinder dataBinder = new DataBinder(target);

    MutablePropertyValues propertyValues = new MutablePropertyValues();
    propertyValues.add("a", "123");
    propertyValues.add("b", "456");
    propertyValues.add("c", "2023/03/19");

    dataBinder.bind(propertyValues);
    System.out.println(target);
}

DataBinder+DirectFieldAccessor

private static void testDataBinder2() {
    Bean2 target = new Bean2();
    DataBinder dataBinder = new DataBinder(target);
    // 改成走DirectFieldAccessor方式
    dataBinder.initDirectFieldAccess();

    MutablePropertyValues propertyValues = new MutablePropertyValues();
    propertyValues.add("a", "123");
    propertyValues.add("b", "456");
    propertyValues.add("c", "2023/03/19");

    dataBinder.bind(propertyValues);
    System.out.println(target);
}

ServletRequestDataBinder

private static void testWebDataBinder() {
    Bean1 target = new Bean1();
    ServletRequestDataBinder servletRequestDataBinder = new ServletRequestDataBinder(target);

    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("a", "123");
    request.setParameter("b", "456");
    request.setParameter("c", "2023/03/19");

    ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
    servletRequestDataBinder.bind(propertyValues);

    System.out.println("target = " + target);
}

WebDataBinder

准备对象

@Data
static class User {
    private Date birthday;
    private Address address;
}

@Data
static class Address {
    private String name;
}

正常绑定

private static void testWebDataBinder2() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    // 无法解析
    request.setParameter("birthday", "1994|07|08");
    request.setParameter("address.name", "上海");


    User target = new User();
    ServletRequestDataBinder servletRequestDataBinder = new ServletRequestDataBinder(target);
    ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
    servletRequestDataBinder.bind(propertyValues);

    System.out.println("target = " + target);
}

使用无参工厂进行绑定

@SneakyThrows
private static void testWebDataBinderFactory() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    // 无法解析
    request.setParameter("birthday", "1994|07|08");
    request.setParameter("address.name", "上海");

    User target = new User();
    ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(null, null);
    WebDataBinder webDataBinder = servletRequestDataBinderFactory.createBinder(new ServletWebRequest(request), target, "target");

    ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
    webDataBinder.bind(propertyValues);
    System.out.println("target = " + target);
}

使用带@InitBinder的工厂进行绑定

准备时间格式化器

@Slf4j
@RequiredArgsConstructor
public class MyDateFormatter implements Formatter<Date> {

    private final String desc;

    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        log.warn("=====>进入了:{}", desc);
        return DateUtil.parse(text, "yyyy|MM|dd");
    }

    @Override
    public String print(Date object, Locale locale) {
        log.warn("=====>进入了:{}", desc);
        return DateUtil.format(object, "yyyy|MM|dd");
    }
}

准备局部@InitBinder

static class MyController {

    @InitBinder
    public void a(WebDataBinder webDataBinder) {
        // 扩展dataBinder的转换器
        webDataBinder.addCustomFormatter(new MyDateFormatter("MyController下@InitBinder进行扩展"));
    }

}

测试

@SneakyThrows
private static void testInitBinder() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("birthday", "1994|07|08");
    request.setParameter("address.name", "上海");

    User target = new User();

    InvocableHandlerMethod invocableHandlerMethod = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("a", WebDataBinder.class));
    ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(
            CollUtil.newArrayList(invocableHandlerMethod),
            null);
    WebDataBinder webDataBinder = servletRequestDataBinderFactory.createBinder(new ServletWebRequest(request), target, "target");

    ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
    webDataBinder.bind(propertyValues);
    System.out.println("target = " + target);
}

使用带ConversionService的工厂进行绑定

@SneakyThrows
private static void testConversionService() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("birthday", "1994|07|08");
    request.setParameter("address.name", "上海");

    User target = new User();

    FormattingConversionService formattingConversionService = new FormattingConversionService();
    formattingConversionService.addFormatter(new MyDateFormatter("FormattingConversionService进行扩展"));

    ConfigurableWebBindingInitializer configurableWebBindingInitializer = new ConfigurableWebBindingInitializer();
    configurableWebBindingInitializer.setConversionService(formattingConversionService);

    ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(
            null,
            configurableWebBindingInitializer);
    WebDataBinder webDataBinder = servletRequestDataBinderFactory.createBinder(new ServletWebRequest(request), target, "target");

    ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
    webDataBinder.bind(propertyValues);
    System.out.println("target = " + target);
}

使用带@InitBinder和ConversionService的工厂进行绑定

private static void testInitBinderAndConversionService() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("birthday", "1994|07|08");
    request.setParameter("address.name", "上海");

    User target = new User();

    InvocableHandlerMethod invocableHandlerMethod = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("a", WebDataBinder.class));

    FormattingConversionService formattingConversionService = new FormattingConversionService();
    formattingConversionService.addFormatter(new MyDateFormatter("FormattingConversionService进行扩展"));
    ConfigurableWebBindingInitializer configurableWebBindingInitializer = new ConfigurableWebBindingInitializer();
    configurableWebBindingInitializer.setConversionService(formattingConversionService);

    // @InitBinder优先级比ConversionService高
    ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(
            CollUtil.newArrayList(invocableHandlerMethod),
            configurableWebBindingInitializer);
    WebDataBinder webDataBinder = servletRequestDataBinderFactory.createBinder(new ServletWebRequest(request), target, "target");

    ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
    webDataBinder.bind(propertyValues);
    System.out.println("target = " + target);
}

DefaultFormattingConversionService

@SneakyThrows
private static void testDefaultConversionService() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("birthday", "1994|07|08");
    request.setParameter("address.name", "上海");

    User target = new User();

    // 需要搭配注解  @DateTimeFormat(pattern = "yyyy|MM|dd")     springmvc
    DefaultFormattingConversionService defaultFormattingConversionService = new DefaultFormattingConversionService();
    ConfigurableWebBindingInitializer configurableWebBindingInitializer = new ConfigurableWebBindingInitializer();
    configurableWebBindingInitializer.setConversionService(defaultFormattingConversionService);

    ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(
            null,
            configurableWebBindingInitializer);
    WebDataBinder webDataBinder = servletRequestDataBinderFactory.createBinder(new ServletWebRequest(request), target, "target");

    ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
    webDataBinder.bind(propertyValues);
    System.out.println("target = " + target);
}

ApplicationConversionService

@SneakyThrows
private static void testApplicationConversionService() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("birthday", "1994|07|08");
    request.setParameter("address.name", "上海");

    User target = new User();

    // 需要搭配注解  @DateTimeFormat(pattern = "yyyy|MM|dd")   springboot
    ApplicationConversionService applicationConversionService = new ApplicationConversionService();
    ConfigurableWebBindingInitializer configurableWebBindingInitializer = new ConfigurableWebBindingInitializer();
    configurableWebBindingInitializer.setConversionService(applicationConversionService);

    ServletRequestDataBinderFactory servletRequestDataBinderFactory = new ServletRequestDataBinderFactory(
            null,
            configurableWebBindingInitializer);
    WebDataBinder webDataBinder = servletRequestDataBinderFactory.createBinder(new ServletWebRequest(request), target, "target");

    ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
    webDataBinder.bind(propertyValues);
    System.out.println("target = " + target);
}

@InitBinder配置方式

@InitBinder 的来源有两个

  1. @ControllerAdvice中的@InitBinder方法,由RequestMappingHandlerAdapter在初始化时解析并记录。
  2. Controller中的@InitBinder,由RequestMappingHandlerAdapter在控制器的任意方法首次执行时解析并记录。

配置@InitBinder

@Configuration
public class WebConfig2 {
    @Controller
    static class Controller1 {
        /**
         * 添加当前Controller局部转换器  由RequestMappingHandlerAdapter在控制器的任意方法首次执行时解析并记录。
         */
        @InitBinder
        public void binder1(WebDataBinder webDataBinder) {
            webDataBinder.addCustomFormatter(new MyDateFormatter("binder1转换器"));
        }

        public void foo() {

        }

    }

    @Controller
    static class Controller2 {
        /**
         * 添加当前Controller局部转换器
         */
        @InitBinder
        public void binder21(WebDataBinder webDataBinder) {
            webDataBinder.addCustomFormatter(new MyDateFormatter("binder2-1转换器"));
        }

        @InitBinder
        public void binder22(WebDataBinder webDataBinder) {
            webDataBinder.addCustomFormatter(new MyDateFormatter("binder2-2转换器"));
        }

        public void bar() {

        }

    }

    @ControllerAdvice
    static class MyControllerAdvice {

        /**
         * 添加全局转换器  由RequestMappingHandlerAdapter在初始化时解析并记录。
         */
        @InitBinder
        public void binder3(WebDataBinder webDataBinder) {
            webDataBinder.addCustomFormatter(new MyDateFormatter("binder3转换器"));
        }
//    @ExceptionHandler
//    @ModelAttribute
    }

}

验证

@Slf4j
public class TestWebDataBinder {

    @SneakyThrows
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(WebConfig2.class);

        RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter();
        requestMappingHandlerAdapter.setApplicationContext(applicationContext);
        requestMappingHandlerAdapter.afterPropertiesSet();

        // 全局的
        log.warn("刚开始...");
        showBindMethods(requestMappingHandlerAdapter);

        Method getDataBinderFactoryMethod = RequestMappingHandlerAdapter.class.getDeclaredMethod("getDataBinderFactory", HandlerMethod.class);
        getDataBinderFactoryMethod.setAccessible(true);

        // 模拟调用Controller1局部方法foo
        getDataBinderFactoryMethod.invoke(requestMappingHandlerAdapter, new HandlerMethod(new WebConfig2.Controller1(), WebConfig2.Controller1.class.getMethod("foo")));
        showBindMethods(requestMappingHandlerAdapter);

        // 模拟调用Controller2局部方法bar
        getDataBinderFactoryMethod.invoke(requestMappingHandlerAdapter, new HandlerMethod(new WebConfig2.Controller2(), WebConfig2.Controller2.class.getMethod("bar")));
        showBindMethods(requestMappingHandlerAdapter);

        applicationContext.close();
    }

    @SneakyThrows
    public static void showBindMethods(RequestMappingHandlerAdapter handlerAdapter) {
        Field initBinderAdviceCacheField = RequestMappingHandlerAdapter.class.getDeclaredField("initBinderAdviceCache");
        initBinderAdviceCacheField.setAccessible(true);

        Map<ControllerAdviceBean, Set<Method>> globalMap = (Map<ControllerAdviceBean, Set<Method>>) initBinderAdviceCacheField.get(handlerAdapter);
        log.warn("ControllerAdvice全局的@InitBinder方法:{}", globalMap.values().stream().flatMap(methods ->
                methods.stream().map(Method::getName)).collect(Collectors.toList()));

        Field initBinderCacheField = RequestMappingHandlerAdapter.class.getDeclaredField("initBinderCache");
        initBinderCacheField.setAccessible(true);

        Map<Class<?>, Set<Method>> localMap = (Map<Class<?>, Set<Method>>) initBinderCacheField.get(handlerAdapter);
        log.warn("控制器局部的@InitBinder方法:{}", localMap.values().stream().flatMap(methods ->
                methods.stream().map(Method::getName)).collect(Collectors.toList()));
    }

}

获取泛型类型

准备泛型类

static class Teacher {

}

static class TeacherMapper {

}

static class BaseDAO<M, E> {

}

static class TeacherDAO extends BaseDAO<TeacherMapper, Teacher> {

}

JDK方式获取泛型类型

private static void testJDKGetGenericTypeName() {
    // 获取带有范型的父类
    Type genericSuperclass = TeacherDAO.class.getGenericSuperclass();
    System.out.println(genericSuperclass);

    if (genericSuperclass instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        System.out.println(parameterizedType.getActualTypeArguments()[0]);
        System.out.println(parameterizedType.getActualTypeArguments()[1]);
    }
}

Spring方式获取泛型类型

private static void testSpringGetGenericTypeName() {
    Class<?>[] typeArguments = GenericTypeResolver.resolveTypeArguments(TeacherDAO.class, BaseDAO.class);
    for (Class<?> typeArgument : typeArguments) {
        System.out.println(typeArgument);
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

层巅余落日

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值