【Bug记录】调用Controller中的方法的Service为null

原文链接:【Bug 记录】调用 Controller 中的方法的 Service 为 null

问题

今天写代码的时候遇到了一个奇怪的Bug,在Controller中调用某个方法时突然报空指针异常,打断点时发现service为null,并尝试了数种注入方式,发现均为null。

原因分析

首先去检查了一下Service上的注解,发现是有 @Service 注解的,因为使用了Lombok的 @RequiredArgsConstructor 注解1,所以在想是不是Lombok的原因,后面去掉该注解,分别尝试了使用 @Autowired 注入、@Resource 注入以及构造器注入,调用时发现仍为null。

上网查询资料后发现关注点错了,既然Service添加了对应注解,且也尝试了不同的注入方法,如果其他方法也均是使用这种方式注入且没出问题的,那就说明问题出在方法上。

在检查方法时发现,在Controller层,方法定义时使用了private 修饰,而不是正常的 public,如下:

查阅资料后发现:容器在扫描Bean并生成代理类的时候,只会生成被 publicprotected 修饰的方法的代理类,被 private 修饰的方法并不会生成代理类。

Q:

<think>好的,我需要解决用户提出的FeignClient远程调用时body字段值为null的问题。首先,我得回忆一下之前处理类似问题的经验,并结合用户提供的引用内容来分析可能的原因和解决方案。 用户提到的引用[1]中,提到了通过配置ObjectMapper来过滤null值,这说明序列化配置可能是一个关键点。Feign默认使用Jackson进行序列化,如果ObjectMapper没有正确配置,可能会导致null值被忽略或者错误处理。需要检查是否在Feign的配置中正确设置了NON_NULL的序列化包含策略。 然后看引用[2],这里有一个使用@GetMapping并带有@RequestBody的例子,但通常GET请求不应该有请求体,这可能导致问题。虽然Feign支持GET请求带body,但某些HTTP实现可能不允许,这可能会引起参数丢失,进而导致null值。用户是否在GET请求中错误地使用了@RequestBody注解? 引用[3]提到了在Controller之前处理body参数可能导致@RequestBody为空的问题。如果在Feign调用过程中,有过滤器或拦截器提前读取了请求体,可能会导致后续Feign无法正确获取body数据,从而字段值为null。需要检查是否有这样的中间件在处理请求体,导致数据被消费掉。 引用[4]指出空字符串被反序列化为null的问题,这可能涉及到序列化配置或者第三方库的处理差异。用户的问题是否涉及到空字符串和null之间的转换?或者是否在数据保存时,null值覆盖了原有数据? 接下来,我需要综合这些可能的原因,逐步分析问题。首先,检查Feign客户端的配置,特别是ObjectMapper的设置,确保其排除了null值。如果配置正确,但问题依然存在,可能需要考虑请求方法是否正确,比如是否误用了GET方法发送带有@RequestBody的请求,这可能导致某些服务器拒绝处理body,进而导致字段丢失。 另外,检查是否有过滤器或拦截器在处理请求时提前读取了输入流,导致Feign无法正确获取请求体数据。例如,在Spring的Filter或Interceptor中调用request.getInputStream()会消耗掉流,后续无法再次读取,导致@RequestBody为空。 还要考虑Feign的日志级别,开启完整的日志可以帮助查看实际发送的请求内容,确认是否确实没有发送null字段,或者是否在传输过程中出现了问题。 最后,如果使用的是旧版Spring Cloud或Feign,可能存在已知的bug,需要升级依赖版本。同时,检查接收方的接口是否能够正确处理null值,或者是否有自己的序列化配置覆盖了Feign的设置。 总结起来,可能的原因包括序列化配置不当、请求方法使用错误、请求体被提前消费、依赖版本问题以及接收方的处理逻辑。针对这些原因,逐一排查并应用相应的解决方案,比如配置ObjectMapper、更换请求方法为POST、避免提前读取请求体、升级依赖版本等,应该能够解决用户遇到的问题。</think>### FeignClient远程调用Body字段值为null的解决方案 #### 一、问题原因分析 1. **序列化配置缺失** Feign默认使用Jackson序列化,未配置过滤null值时可能自动忽略空值字段[^1]。例如未设置`JsonInclude.Include.NON_NULL`会导致包含null值的字段被过滤。 2. **HTTP方法使用不当** 使用GET方法发送包含`@RequestBody`的请求时,部分HTTP客户端或服务端会拒绝处理请求体[^2]。例如: ```java @GetMapping("/api") // GET方法不适合传输Body void example(@RequestBody RequestModel model); ``` 3. **请求体被提前消费** 过滤器/拦截器中调用`request.getInputStream()`会消耗输入流,导致后续`@RequestBody`无法读取[^3]。 4. **Feign版本兼容性问题** Spring Cloud旧版本存在Feign与Jackson的兼容性问题,可能导致空值处理异常[^4]。 --- #### 二、解决方案步骤 ##### 1. 配置Jackson序列化规则 在Feign配置类中显式定义`ObjectMapper`: ```java @Configuration public class FeignConfig extends FeignClientsConfiguration { @Bean public ObjectMapper jacksonObjectMapper() { return new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL); // 仅序列化非空字段 } } ``` 通过`@EnableFeignClients(defaultConfiguration = FeignConfig.class)`启用配置[^1]。 ##### 2. 规范HTTP方法使用 - **强制使用POST方法**:确保Body数据传输符合HTTP规范 ```java @PostMapping("/api") void example(@RequestBody RequestModel model); ``` - **GET请求参数传递**:改用`@RequestParam`或URL路径参数 ```java @GetMapping("/api") void example(@RequestParam("param") String param); ``` ##### 3. 避免请求体重复读取 在过滤器中处理请求体时,使用`ContentCachingRequestWrapper`: ```java public class CustomFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper((HttpServletRequest) request); // 通过wrappedRequest.getContentAsByteArray()读取Body chain.doFilter(wrappedRequest, response); } } ``` 此方案可避免原生输入流被消费[^3]。 ##### 4. 升级依赖版本 检查`spring-cloud-starter-openfeign`版本,建议使用较新版本: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2023.0.0</version> <!-- 示例版本 --> </dependency> ``` ##### 5. 添加Feign日志诊断 application.yml中开启DEBUG日志: ```yaml logging: level: org.springframework.cloud.openfeign: DEBUG ``` 观察控制台输出的实际请求内容,验证字段是否被正确序列化。 --- #### 三、验证方案 1. **单元测试验证** 使用MockMVC测试Feign客户端: ```java @SpringBootTest class FeignClientTest { @Autowired private TestFeignClient feignClient; @Test void testRequestBody() { RequestModel model = new RequestModel(null, "value"); feignClient.callApi(model); // 断点查看传输内容 } } ``` 2. **网络抓包分析** 通过Wireshark或Charles捕获实际网络请求,确认null字段是否被过滤。 --- #### 四、扩展优化建议 1. **自定义编解码器** 实现`feign.codec.Encoder`接口,精细化控制序列化过程: ```java public class CustomEncoder implements Encoder { private final ObjectMapper mapper = new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL); @Override public void encode(Object object, Type bodyType, RequestTemplate template) { // 自定义序列化逻辑 } } ``` 2. **空字符串处理** 若需要区分null和空字符串,添加特殊处理: ```java @Bean public ObjectMapper jacksonObjectMapper() { return new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL) .configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false); } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Bummon.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值