我配置了一个自定义 HandlerMethodArgumentResolver,用于从请求标头获取一些参数,并将它们包装在自定义对象中,以作为参数注入到我的控制器中。
自定义的HandlerMethodArgumentResolver不在主项目中,而是在主应用程序导入的库中。 Pom 和组件扫描都可以,因为其他 Bean 和配置正在处理,没有问题。
在 JVM 上运行它一切正常,但是使用 GraalVM 将其编译为本机应用程序给我带来了麻烦,因为当我运行应用程序时,不会考虑自定义参数解析器,并且注入的自定义对象为 null在运行时。
这是代码。
public class HeaderMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(MyHeader.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
MyHeader header = MyHeaderExtractor.generateFrom(webRequest);
// Makes Validated annotation working on my custom argument
if (parameter.hasParameterAnnotation(Validated.class) || parameter.hasParameterAnnotation(Valid.class)) {
// Validate header
WebDataBinder binder = binderFactory.createBinder(webRequest, header, parameter.getParameterName());
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
return header;
}
/**
* Validate the model attribute if applicable.
* <p>The default implementation checks for {@code @jakarta.validation.Valid},
* Spring's {@link org.springframework.validation.annotation.Validated},
* and custom annotations whose name starts with "Valid".
* @param binder the DataBinder to be used
* @param parameter the method parameter declaration
* @see WebDataBinder#validate(Object...)
* @see SmartValidator#validate(Object, Errors, Object...)
*/
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
for (Annotation ann : parameter.getParameterAnnotations()) {
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
if (validationHints != null) {
binder.validate(validationHints);
break;
}
}
}
}
然后是让 Spring MVC 了解我的参数解析器的配置类。
@EnableWebMvc // Tried with and without EnableWebMvc
@Configuration(proxyBeanMethods = false)
public class HeaderMethodArgumentResolverConfiguration implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
// tried this
resolvers.add(new HeaderMethodArgumentResolver());
// and also this when created as a Bean
resolvers.add(headerMethodArgumentResolver());
}
@Bean // Tried with and without this method
public HeaderMethodArgumentResolver headerMethodArgumentResolver() {
return new HeaderMethodArgumentResolver();
}
}
还有一个示例控制器。
@RestController
@RequestMapping("/api/my-resource")
@Slf4j
public class MyController {
@PostMapping("/example")
public MyResponseObject doSomething(
@Validated({HeaderAsIWant.class}) MyHeader myHeader,
@Validated @RequestBody MyRequestObject request) {
}
}
我是否做错了什么,缺少某些配置,或者是与本机构建的错误/不兼容?
非常感谢大家的回复。
注 1:我从 /actuator/beans 调用中进行了检查,并且我确信 bean 已正确实例化。问题似乎是当请求到来时 Spring 不会调用方法解析器。
注 2:将自定义参数解析器从库移动到主项目可以解决问题。但这对我来说不是一个解决方案,因为我需要在多个微服务中使用此参数解析器,可能不需要复制粘贴它。
我终于弄清楚了如何让它工作,只需在配置 bean 上添加以下内容:
@RegisterReflectionForBinding({HeaderMethodArgumentResolverConfiguration.class})
这是完整的课程:
@Configuration(proxyBeanMethods = false)
@RegisterReflectionForBinding({HeaderMethodArgumentResolverConfiguration.class})
public class HeaderMethodArgumentResolverConfiguration implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new HeaderMethodArgumentResolver());
}
}
可能,鉴于配置 bean 在库中,它需要注册反射信息以发现 addArgumentResolvers 方法或者它实现 WebMvcConfigurer (现在不能确定,但它可以工作!)