Spring Boot 全局控制器建议未在 Spring 上下文中加载

Posted

技术标签:

【中文标题】Spring Boot 全局控制器建议未在 Spring 上下文中加载【英文标题】:Spring Boot global controller advice is not loaded in Spring Context 【发布时间】:2019-01-15 07:05:04 【问题描述】:

我有一个控制器来接收 POST 请求并返回 JSON 输出。在控制器类中实现的异常处理程序运行良好。

我尝试使用@ControllerAdvice 注释添加全局异常处理,但这在我的解决方案中不起作用。我认为没有加载全局异常处理程序类。

下面是我的控制器类:

package hello;

@Controller
@RequestMapping("/v1")
public class MyController 

  @RequestMapping(value = "/saveEmployee", method = RequestMethod.POST, produces = "application/json")
  @ResponseBody
  public String saveEmployee(@Valid @RequestBody Employee employee) 
    return " \"name\":\"" + employee.getEmail() + "\"";
  

下面是全局异常处理类:

package util;

@ControllerAdvice
public class MyControllerAdvice 

  @ExceptionHandler(MethodArgumentNotValidException.class)
  // @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ResponseBody
  public String processValidationError(MethodArgumentNotValidException ex) 
    BindingResult result = ex.getBindingResult();
    FieldError fieldError = result.getFieldError();
    String code = fieldError.getCode();
    String field = fieldError.getField();
    String message = fieldError.getDefaultMessage();
    message = " \"Code\":\"" + code + "\",\"field\":\"" + field + "\",\"Message\":\"" + message + "\"";
    return message;
  

以下是我的配置:

package hello;

@SpringBootApplication
public class Application 

    @Bean
    public MessageSource messageSource() 
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    

    @Bean
    public LocalValidatorFactoryBean validator() 
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource());
        return bean;
    

    public static void main(String[] args) 
        SpringApplication.run(Application.class, args);
    

Spring boot 版本:2.0.3.RELEASE

【问题讨论】:

这是我在传递给控制器​​的无效参数中收到的内容“[nio-8080-exec-2] .wsmsDefaultHandlerExceptionResolver:已解决由处理程序执行引起的异常:org.springframework.web.bind.MethodArgumentNotValidException :方法中索引 0 处的参数验证失败:“@Piotr Rogowski 您能发布您对上述控制器的 Spring 配置吗?我看到控制器位于名为hello 的包中,而异常处理程序位于名为util 的包中。我的第一个问题是您没有将异常处理程序带入上下文。 【参考方案1】:

我认为您在这里遇到的问题正是您发现的问题。 MyControllerAdvice 类未加载。根据您发布的代码,我推断您的项目结构类似于:

src
  main
    java
      hello
        Application.java
        MyController.java
      util
        MyControllerAdvice.java

因为您的 SpringBootApplication 类上有 SpringBootApplication 注释,所以 Spring Boot 使用它作为创建 bean 的起点,它会查找带有 @Controller@ControllerAdvice 等注释的类(以及其他)添加到它的上下文中。

有许多不同的配置方法,但如果您刚开始或正在处理一个相对较小的项目,最简单的方法是对其进行重组以适应 Spring 在此处的建议:

https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-structuring-your-code.html

该页面很短,值得一读以了解背景,但本质是您希望您的@SpringBootApplication注释类与您希望Spring管理的所有类在同一个包或父包中,上面的页面举个例子:

com
 +- example
   +- myapplication
     +- Application.java
     |
     +- customer
     | +- Customer.java
     | +- CustomerController.java
     | +- CustomerService.java
     | +- CustomerRepository.java
     |
     +- order
       +- Order.java
       +- OrderController.java
       +- OrderService.java
       +- OrderRepository.java

因此,在您的情况下,这意味着将 util 包移动到 hello 下,例如:

src
  main
    java
      hello
        Application.java
        MyController.java
        util
          MyControllerAdvice.java

或者为helloutil 创建一个父包并将Application 移动到该包,例如:

src
  main
    java
      example
        Application.java
        hello
          MyController.java
        util
          MyControllerAdvice.java

【讨论】:

是的。这行得通。我已经重新构建了整个项目。现在 Spring 引导已将 @ControllerAdvice 添加到上下文中。谢谢【参考方案2】:

试试这个:@ControllerAdvice("hello")

根据ControllerAdvice的文档:

将包含属于这些基础包或其子包的控制器,例如:@ControllerAdvice(basePackages="org.my.pkg") 或 @ControllerAdvice(basePackages="org.my.pkg", " org.my.other.pkg")。 value 是该属性的别名,只是允许更简洁地使用注释。 还可以考虑使用 basePackageClasses() 作为基于字符串的包名称的类型安全替代方案。

【讨论】:

我已经试过了。但没有区别。我观察到的是“DefaultHandlerExceptionResolver:由处理程序执行引起的已解决异常:org.springframework.web.bind.MethodArgumentNotValidException:”@Hank 试试把MyControllerAdvice放到MyController的同一个包里。我可以重现您的问题并通过我的解决方案解决它。 是的。我只是尝试了与您提到的相同的方法,并且有效。谢谢。但是你有什么理由不把 MyControllerAdvice 放在不同的包中工作吗? @汉克 @DaveyDaveDave 的回答解释了我的观点。 Spring 使用 @SpringBootApplication 类的包作为默认根包,并扫描该根包下的 Beans/Configurations。这就是为什么你的@ControllerAdvice 一开始没有被Spring 找到:)。但也请注意,仅将@SpringBootApplication 拉到上层是不够的,@ControllerAdvice 默认只影响@Controller 的相同包(和子包),除非你正确配置basePackages 是的。这是一个很好的建议。我正确配置了基础包并对问题进行了排序。@Hank【参考方案3】:

你确定它会抛出 MethodArgumentNotValidException 吗?像这样制作默认处理程序

@ExceptionHandler(value = Exception.class)
public ResponseEntity<Object> exception(Exception exception) 
    return new ResponseEntity<>(exception, HttpStatus.BAD_REQUEST)

【讨论】:

没有。这行不通。是的,我确定它会触发 MethodArgumentNotValidException @GerNik 如果它没有被其他处理程序捕获,这有助于我获得洞察力。

以上是关于Spring Boot 全局控制器建议未在 Spring 上下文中加载的主要内容,如果未能解决你的问题,请参考以下文章

JSP 文件未在 Spring Boot Web 应用程序中呈现

使用邮递员返回错误请求,但未在单元测试 Spring Boot 中返回

多部分表单数据输入 Java 模型属性未在请求类中注入元素 - Spring Boot

Spring boot 配置文件详解 (properties 和yml )

Spring/Boot - JWT 未在 GET 请求中获取

Spring Boot 控制器建议 - 如何返回 XML 而不是 JSON?