0%

3.17.4 @ControllerAdvice注解

3.17.4 @ControllerAdvice注解

org.springframework.web.bind.annotation.Controlleradvice注解是Spring3.2提供的新注解,它是一个控制器增强功能注解。该注解源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Target(ElementType.TYPERE)
@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{}
}

该注解使用@Component注解,也就是说可以使用<context: component-scan>扫描该注解。 Spring官方文档说明,扫描到@Controlleradvice注解之后,会将@ControllerAdvice注解修饰的类的内部使用@ExceptionHandler@InitBinder@ModelAttribute注解的方法应用到所有的请求处理方法上。
在实际开发中@ExceptionHandler注解的功能最强大,另外两个用处不大。

示例 @ControllerAdvice处理异常

新建一个项目ControllerAdviceTest,加入所需的jar文件,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>异常处理示例</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"></script>
<script type="text/javascript">
$(function() {
$("#search").click(
function() {
$.post("${pageContext.request.contextPath}/search", null,
function(data) {
// 处理异常
if (data.message) {
alert("与服务器交互出现异常:" + data.message);
} else {
// 获取服务器响应,显示所有订单信息

}
}, "json");
});
})
</script>
</head>
<body>
<br>
<a href="find">@ControllerAdvice异常处理</a>
<hr>
<button id="search">查询订单(返回JSON)</button>
</body>
</html>

需要注意id=" search"的按钮,使用jQuery进行异步査询订单时,如果抛岀异常,则获取返回的JSON数据并提示错误

BookController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.fkit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class BookController
{
@GetMapping("/find")
public String find() throws Exception
{
// 除零异常,当前类中没有写异常处理函数,
// 也没有通过继承得到异常处理函数
// 这个异常将由@ControllerAdvice注解的类来处理
@SuppressWarnings("unused")
int i = 5 / 0;
return "success";
}
}

BookController处理"find"请求,在请求处理方法中简单地模拟了一个异常。 BookController中并没有@ExceptionHandler注解修饰的方法,抛出的异常会由@Controlleradvice注解修饰的类中的@ExceptionHandle注解修饰的方法进行处理。

OrderException.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.fkit.controller;

public class OrderException extends RuntimeException
{
private static final long serialVersionUID = 6857573209047095609L;
public OrderException()
{
super();
}

public OrderException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace)
{
super(message, cause, enableSuppression, writableStackTrace);
}
// 重载方法
public OrderException(String message, Throwable cause)
{
super(message, cause);
}

public OrderException(String message)
{
super(message);
}

public OrderException(Throwable cause)
{
super(cause);
}
}

OrderException是一个自定义异常类型,继承自RuntimeException.

OrderController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.fkit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class OrderController
{
@PostMapping("/search")
public String search() throws Exception
{
try
{
// 除零异常
@SuppressWarnings("unused")
int i = 5 / 0;
return "success";
} catch (Exception e)
{
e.printStackTrace();
// 抛出异常,将由@ControllerAdvice注解的类进行异常处理
throw new OrderException("订单查询失败!");
}
}
}

OrderController处理"search"请求,在请求处理方法中简单地模拟了一个异常,被捕捉后抛出OrderException自定义异常类型。

GlobalExceptionHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package org.fkit.controller;

import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

// GlobalExceptionHandler类使用了@ControllerAdvice注解来修饰,
// 其会被<context:component-scan>扫描,
// 这使得该类中使用@ExceptionHandler注解修饰的方法都被应用到所有请求处理方法上
// 也就是所有请求处理方法抛出的异常都将由该类中对应的@ExceptionHandler注解修饰的方法处理.
@ControllerAdvice
public class GlobalExceptionHandler
{
// 处理Exception类型异常
@ExceptionHandler(value = Exception.class)
public ModelAndView globalErrorHandler(Exception e) throws Exception
{
ModelAndView mav = new ModelAndView();
mav.addObject("ex", e);
mav.setViewName("error");
return mav;
}
// 处理OrderException自定义异常
@ExceptionHandler(value = OrderException.class)
// 返回的结果将会被封装成JSON数据,并返回给客户端
@ResponseBody
public Object OrderErrorHandler(Exception e) throws Exception
{
// 创建返回对象Map并设置属性,会被@ResponseBody注解转换为JSON返回
Map<String, Object> map = new HashMap<>();
map.put("code", 100);
map.put("message", e.getMessage());
map.put("data", "请求失败");
return map;
}
}

GlobalExceptionHandler类使用了@ControllerAdvice注解来修饰,则该类会被<context: component-scan>扫描,该类中使@ExceptionHandler注解修饰的方法将被应用到所有请求处理方法上。
GlobalExceptionHandler类中定义了两个方法:

  • 第一个方法globalErrorHandler使用@ExceptionHandler注解修饰,value=Exception.class属性表示该方法处理所有Exception类型的异常,处理方式和之前一致,将异常信息对象保存到Model,并返回异常处理页面error.jsp;
  • 第二个方法OrderErrorHandler使用@ExceptionHandler注解修饰,value=OrderException.class表示该方法处理OrderException自定义类型的异常,此处的处理方式和之前不同,这里创建一个Map对象保存信息并返回,由于方法使用了@ResponseBody注解,返回的Map对象会被转成JSON数据。

部署ControllAdviceTest这个Web应用,在浏览器中输入如下URL来测试应用:

1
http://localhost:8080/ControllerAdviceTest/
  • 单击"@ControllerAdvice异常处理"超链接,发送"find"请求, BookControllerfind()方法处理请求,抛出异常,异常被@ControllerAdvice注解修饰的GlobalExceptionHandler类中@ExceptionHandler(value = Exception.class)注解修饰的globalErrorHandler方法捕获,处理之后跳转到error.jsp页面.
  • 再次请求index.jsp页面,单击"查询订单(返回JSON)"按钮,发送"search"请求,OrderControllersearch()方法处理请求,抛出自定义异常OrderException,该异常被@ControllerAdvice注解修饰的GlobalExceptionHandler类中@ExceptionHandler(value = OrderException.class)注解修饰的OrderErrorHandler方法捕获,返回JSON信息.

原文链接: 3.17.4 @ControllerAdvice注解