spring自定义pojo参数映射原理

本文详细探讨了Spring MVC中控制器方法参数的映射原理,从HttpServlet到DispatcherServlet,再到HandlerAdapter和HandlerExecutionChain。讲解了ModelAttributeMethodProcessor如何处理@ModelAttribute注解的参数,以及如何通过WebDataBinder进行数据绑定。还提到了如何自定义参数转换器,通过WebMvcConfigurer接口的addFormatters方法添加自定义Converter,解决了lambda表达式作为Converter时遇到的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自定义pojo参数映射原理

HttpServlet接口

  1. 由spring实现FrameworkServlet get/post -> doService

  2. 衍生到子类DispatcherServlet实现

  3. doDispatcher来分配并执行方法,getHandler(processedRequest)获取合适的handlerExecutionChain来执行请求

  4. 找到HandlerAdapter来屏蔽方法名不同带来的差距,最终找到RequestMappingHandlerAdapter

  5. 判断是否要执行前置通知preHandler处理方法

  6. 执行目标方法mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

  7. RequestMappingHandlerAdapter的invokeHandlerMethod方法下的invocableMethod.invokeAndHandle(webRequest, mavContainer);

  8. 开始执行时第一步就是获取参数InvocableHandlerMethod#invokeForRequest方法中getMethodArgumentValues(request, mavContainer, providedArgs);获取请求参数

  9. 通过初始化时就注入的参数解析器HandlerMethodArgumentResolver来遍历寻找合适的参数解析器解析参数

  10. 找到ServletModelAttributeMethodProcessor 来处理并放入缓存ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>

    该解析器是ModelAttributeMethodProcessor的字类

  11. ModelAttributeMethodProcessor#resolveArgument解析参数

  12. 使用WebDataBinder来绑定参数到指定pojo

HandlerExecutionChain

由Handler对象及Handler拦截器构成的Handler执行链,由HandlerMappinggetHandler方法返回获取

HandlerMapping

Interface to be implemented by objects that define a mapping between requests and handler objects.

一个定义请求与处理器对象映射的将被实现的接口

HandlerAdapter

MVC framework SPI, allowing parameterization of the core MVC workflow

支持核心MVC工作流参数化的MVC框架SPI

java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。这有点类似IOC的思想,将装配的控制权移到了程序之外。

ModelAttributeMethodProcessor

Resolve {@code @ModelAttribute} annotated method arguments and handle return values from {@code @ModelAttribute} annotated methods.

处理被@ModelAttribute注解标注的方法看书映射

ServletModelAttributeMethodProcessor

A Servlet-specific {@link ModelAttributeMethodProcessor} that applies data binding through a WebDataBinder of type {@link ServletRequestDataBinder}.

一个通过WebDataBinder来绑定参数的特定处理器与父类相比,并没有在参数上添加相关注解

GenericConversionService

Base {@link ConversionService} implementation suitable for use in most environments.

基础参数转换类

提交时input框的name通过级联属性来触发映射到内部的pojo中例如pet.name

静态资源位置暂定resource/resource优先于resource/static

自定义参数转换格式

WebMvcConfigurer

自定义mvc配置器

addFormatters

Add {@link Converter Converters} and {@link Formatter Formatters} in addition to the ones registered by default.

向容器中添加自定义WebMvcConfigurer 通过重写addFormatters方法来自定义参数转换规则

多参数模板lambda表达式写法:

registry.addConverter((Converter<String, Pet>)(s) ->{
                    if (!StringUtils.isEmpty(s)){
                        Pet pet = new Pet();
                        String[] split = s.split(",");
                        pet.setName(split[0]);
                        pet.setAge(Integer.parseInt(split[1]));
                        return pet;
                    }
                    return null;
                });

出错

java.lang.IllegalArgumentException: Unable to determine source type and target type for your Converter

lambda表达式实际会被转为一个由static标注的私有方法进而产生合适的类,该方法是声明类的一部分,spring无法检索/访问

所以可通过

Converter<String, Pet> myConverter = source -> {
                    if (!StringUtils.isEmpty(source)) {
                        Pet pet = new Pet();
                        String[] split = source.split(",");
                        pet.setName(split[0]);
                        pet.setAge(Integer.parseInt(split[1]));
                        return pet;
                    }
                    return null;
                };
                registry.addConverter(myConverter);

这种方法来解决,解决失败。

注入实现类还是直接实现而不是lambda表达式为好

解决

registry.addConverter(new Converter<String, Pet>() {
                    @Override
                    public Pet convert(String source) {
                        if (!StringUtils.isEmpty(source)) {
                            Pet pet = new Pet();
                            String[] split = source.split(",");
                            pet.setName(split[0]);
                            pet.setAge(Integer.parseInt(split[1]));
                            return pet;
                        }
                        return null;
                    }
                });

在解析时将自定义converter也注入了

### 创建和使用Spring框架中的自定义配置 #### 创建配置 为了管理特定应用程序的配置项,在Java代码中可以创建一个专门用于保存这些配置参数POJO(Plain Old Java Object),即配置。此过程涉及引入必要的包并标注相应的注解。 ```java import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "myapp") public class MyAppConfig { private String apiUrl; private String apiKey; // Getters and Setters are omitted here. } ``` 上述代码展示了`MyAppConfig`,它被标记为`@Configuration`以便让Spring容器识别这是一个可装配组件;同时应用了`@ConfigurationProperties`来绑定前缀为`myapp`的应用程序属性到该对象字段上[^1]。 #### 定义配置文件条目 接着需在项目的资源目录下编辑或新增`application.properties`文件,并加入如下所示的相关键值对: ```properties myapp.api-url=https://api.example.com/v1 myapp.api-key=your_api_key_here ``` 这里设置的具体名称应当与之前提到过的配置成员变量相匹配,只是形式上采用了连字符分隔的小风格[^3]。 #### 注入配置实例 当希望在整个应用程序的不同部分访问已加载好的配置数据时,则可以在其他Bean里声明依赖关系并通过构造器注入或者字段注入的方式获取对应的配置实例。 ```java @Autowired private MyAppConfig myAppConfig; // 或者采用更推荐的做法——构造函数注入 public MyClass(MyAppConfig config){ this.myAppConfig = config; } // 后续可以通过调用get方法获得具体的配置信息 String url = myAppConfig.getApiUrl(); String key = myAppConfig.getApiKey(); ``` 这样就完成了整个流程:从定义外部化的配置直到最终将其映射至内部使用的实体之中[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值