一、简要说明
如何实现网上文章基本是随便一搜就可以很快找到, 这里不再赘述.
二、Spring-web和Spring-webmvc
通过idea查看到两个注解位于 spring-web-5.2.2.RELEASE.jar
包内,.
而这里maven依赖有spring web 和spring webmvc 两个包;
查询到spring web主要提供了HTTP的功能集成,
而spring-webmvc基于spring-web, 从而提供servlet功能. (参考: Maven dependency spring-web vs spring-webmvc)
2.2.5 Web
The Web layer consists of the spring-web, spring-webmvc, spring-websocket, and spring-webmvc-portlet modules.The spring-web module provides basic web-oriented integration features such as multipart file upload functionality and the initialization of the IoC container using Servlet listeners and a web-oriented application context. It also
contains an HTTP client and the web-related parts of Spring’s remoting support.The spring-webmvc module (also known as the Web-Servlet module) contains Spring’s model-view-controller (MVC) and REST Web Services implementation for web applications. Spring’s MVC framework provides a clean > separation between domain model code and web forms and integrates with all of the other features of the Spring Framework.
The spring-webmvc-portlet module (also known as the Web-Portlet module) provides the MVC implementation to be used in a Portlet environment and mirrors the functionality of the Servlet-based spring-webmvc module.
- 文档地址(这里有很多值得阅读地方): https://docs.spring.io/spring/docs/4.3.22.RELEASE/spring-framework-reference/htmlsingle/#overview-web
三、两个注解
我们已经知道主要使用@ControllerAdvice
和@ExceptionHandler
这两个注解来实现全局异常类的定义.
- @ControllerAdvice
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
- @ExceptionHandler
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
Class<? extends Throwable>[] value() default {};
}
通过在spring framework - web mvc官方文档中搜索@ControllerAdvice
得到了一些信息:
(以下部分摘自Spring文档)
- Resolves exceptions by invoking an @ExceptionHandler method in a @Controller or a @ControllerAdvice class. See @ExceptionHandler methods.
1.3.6. Exceptions (@ExceptionHandler methods)
文档中说"@Controller和@ControllerAdvice类可以具有 @ExceptionHandler处理控制器方法异常的方法", 但是经过尝试发现@Controller和@ExceptionHandler组合并没有实现.
-
两种匹配异常的方式
-
直接匹配
该异常可能与正在传播的顶级异常(即,直接IOException引发的异常)匹配,也可能与顶级包装器异常(例如,IOException包装在中IllegalStateException)的直接原因匹配 。
对于匹配的异常类型,如前面的示例所示,最好将目标异常声明为方法参数。当多个异常方法匹配时,根源异常匹配通常比原因异常匹配更可取。更具体地,ExceptionDepthComparator用于根据异常从引发的异常类型的深度对异常进行排序。
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
- 注释声明可以缩小异常类型以使其匹配,如以下示例所示:
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
// ...
}
- 您甚至可以使用带有非常通用的参数签名的特定异常类型的列表,如以下示例所示:
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
// ...
}
- 根和原因异常匹配之间的区别可能令人惊讶。
在IOException前面显示的变体中,通常以实际FileSystemException或RemoteException实例作为参数来调用该方法,因为这两个方法都从扩展IOException。但是,如果任何此类匹配异常都在本身为的包装器异常中传播IOException,则传入的异常实例就是该包装器异常。
在handle(Exception)变体中,行为甚至更简单。在包装方案中,总是使用包装程序异常来调用此方法,ex.getCause()在这种情况下,将找到实际匹配的异常。仅当将它们作为顶级异常抛出时,传入的异常才是实际的FileSystemException或 RemoteException实例。
我们通常建议您在参数签名中尽可能具体,以减少根类型和原因异常类型之间不匹配的可能性。考虑将多重匹配方法分解为单个@ExceptionHandler 方法,每个方法都通过其签名匹配单个特定的异常类型。
在多种@ControllerAdvice安排中,我们建议声明@ControllerAdvice优先级并以相应顺序声明您的主根异常映射。尽管根异常匹配优先于原因,但这是在给定控制器或@ControllerAdvice类的方法之间定义的。这意味着优先级较高的@ControllerAdvicebean 上的原因匹配优于优先级 较低的@ControllerAdvicebean 上的任何匹配(例如,根) 。
- 最后但并非最不重要的一点是,@ExceptionHandler方法实现可以选择通过以原始形式重新抛出异常来退出处理给定异常实例。在仅对根级别匹配或无法静态确定的特定上下文中的匹配感兴趣的情况下,这很有用。重新抛出的异常会在其余的解决方案链中传播,就像给定的@ExceptionHandler方法最初不会匹配一样。
@ExceptionHandlerSpring MVC中对方法的支持建立在HandlerExceptionResolver机制DispatcherServlet 级别上。
- @ExceptionHandler 支持的方法参数:(见原文)
- @ExceptionHandler 方法支持的返回值:(见原文)
REST API exceptions
REST服务的常见要求是在响应正文中包含错误详细信息。Spring框架不会自动执行此操作,因为响应主体中错误详细信息的表示是特定于应用程序的。但是,a @RestController可以使用@ExceptionHandler带有ResponseEntity返回值的方法来设置响应的状态和主体。也可以在@ControllerAdvice类中声明此类方法以将其全局应用。
在响应主体中实现具有错误详细信息的全局异常处理的应用程序应考虑extend ResponseEntityExceptionHandler,它提供对Spring MVC引发的异常的处理,并提供用于自定义响应主体的钩子。要使用此功能,请创建一个的子类 ResponseEntityExceptionHandler,用对其进行注释@ControllerAdvice,覆盖必要的方法,然后将其声明为Spring bean。
1.3.7. Controller Advice
通常@ExceptionHandler,,@InitBinder和@ModelAttribute方法适用于@Controller声明它们的类(或类层次结构)。如果要使此类方法更全局地应用(跨控制器),则可以在带有@ControllerAdvice或注释的类中声明它们@RestControllerAdvice。
@ControllerAdvice带有注释@Component,这意味着可以通过组件扫描将此类注册为Spring Bean 。@RestControllerAdvice是用@ControllerAdvice和注释的组合注释@ResponseBody,从本质上讲,这意味着 @ExceptionHandler方法是通过消息转换(与视图解析或模板渲染相比)呈现给响应主体的。
在启动时,用于@RequestMapping和@ExceptionHandler 方法的基础结构类会检测带有注释的Spring bean @ControllerAdvice,然后在运行时应用其方法。全局@ExceptionHandler方法(来自@ControllerAdvice)适用于局部方法(来自@Controller)。相比之下,全局@ModelAttribute 和@InitBinder方法先于局部方法。
默认情况下,@ControllerAdvice方法适用于每个请求(即所有控制器),但是您可以通过使用批注上的属性将其范围缩小到控制器的子集,如以下示例所示:
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}
前面示例中的选择器在运行时进行评估,如果广泛使用,可能会对性能产生负面影响。有关@ControllerAdvice 更多详细信息,请参见 javadoc。