业务异常处理
# 原理
controller调用Service, 各Service之间调用,哪一层业务逻辑不对,就可往外抛异常,程序不会往下执行。然后通过全局的异常拦截器,对异常进行处理。
- 异常信息的规范化,如状态码、异常内容
- 事务回滚默认对RuntimeException有效
# 定义异常类
/**
* 业务异常
*
* @author L.cm
*/
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 2359767895161832954L;
@Nullable
private final R<?> result;
public ServiceException(R<?> result) {
super(result.getMsg());
this.result = result;
}
public ServiceException(IResultCode rCode) {
this(rCode, rCode.getMsg());
}
public ServiceException(IResultCode rCode, String message) {
super(message);
this.result = R.fail(rCode, message);
}
public ServiceException(String message) {
super(message);
this.result = null;
}
public ServiceException(Throwable cause) {
this(cause.getMessage(), cause);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
doFillInStackTrace();
this.result = null;
}
@Nullable
@SuppressWarnings("unchecked")
public <T> R<T> getResult() {
return (R<T>) result;
}
/**
* 抛异常的时候,不打印堆栈信息,可以提高性能
* @return Throwable
*/
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
public Throwable doFillInStackTrace() {
return super.fillInStackTrace();
}
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
guns7写法
# 1、定义异常接口,供枚举类实现
/**
* 异常枚举格式规范,每个异常枚举都要实现这个接口
* <p>
* 为了在抛出ServiceException的时候规范抛出的内容
* <p>
* ServiceException抛出时必须为本接口的实现类
*
* @author fengshuonan
* @date 2020/10/14 21:41
*/
public interface AbstractExceptionEnum {
/**
* 获取异常的状态码
*
* @return 状态码
* @author fengshuonan
* @date 2020/10/14 21:42
*/
String getErrorCode();
/**
* 获取给用户提示信息
*
* @return 提示信息
* @author fengshuonan
* @date 2020/10/14 21:42
*/
String getUserTip();
}
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
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
# 2、定义公共返回值,子类可以再继承
/**
* http响应结果封装
*
* @author fengshuonan
* @date 2020/10/17 17:33
*/
@Data
public class ResponseData {
/**
* 请求是否成功
*/
private Boolean success;
/**
* 响应状态码
*/
private String code;
/**
* 响应信息
*/
private String message;
/**
* 响应对象
*/
private Object data;
public ResponseData() {
}
public ResponseData(Boolean success, String code, String message, Object data) {
this.success = success;
this.code = code;
this.message = message;
this.data = data;
}
}
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
40
41
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
40
41
# 3、定义异常通用返回值
/**
* 请求失败的结果包装类
*
* @author fengshuonan
* @date 2020/10/16 16:26
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class ErrorResponseData extends ResponseData {
/**
* 异常的具体类名称
*/
private String exceptionClazz;
/**
* 异常的提示信息
*/
private String exceptionTip;
/**
* 跟项目有关的具体异常位置
* <p>
* 一般是堆栈中第一个出现项目包名的地方
*/
private String exceptionPlace;
public ErrorResponseData(String code, String message) {
super(Boolean.FALSE, code, message, null);
}
public ErrorResponseData(String code, String message, Object object) {
super(Boolean.FALSE, code, message, object);
}
}
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
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
# 4、实现接口,定义异常信息枚举常量
/**
* 参数校验错误
*
* @author fengshuonan
* @date 2020/10/16 10:53
*/
@Getter
public enum ValidatorExceptionEnum implements AbstractExceptionEnum {
/**
* Parameter传参,请求参数缺失异常
*/
MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "01", "Parameter传参,请求参数缺失异常,参数名:{},类型为:{}"),
/**
* 请求数据经过httpMessageConverter出错
*/
HTTP_MESSAGE_CONVERTER_ERROR(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "02", "请求Json数据格式错误或Json字段格式转化问题"),
/**
* 不受支持的媒体类型
*/
HTTP_MEDIA_TYPE_NOT_SUPPORT(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "03", "请求的http media type不合法"),
/**
* 不受支持的http请求方法
*/
HTTP_METHOD_NOT_SUPPORT(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "04", "当前接口不支持{}方式请求"),
/**
* 404找不到资源
*/
NOT_FOUND(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "05", "404:找不到请求的资源"),
/**
* 参数校验失败
* <p>
* 拦截@Valid和@Validated校验失败返回的错误提示
*/
VALIDATED_RESULT_ERROR(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "06", "参数校验失败,请检查参数的传值是否正确,具体信息:{}"),
/**
* 数据库字段值唯一性校验出错,参数不完整
*/
TABLE_UNIQUE_VALIDATE_ERROR(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "07", "数据库字段值唯一性校验出错,具体信息:{}"),
/**
* 验证码为空
*/
CAPTCHA_EMPTY(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "08", "验证码参数不能为空"),
/**
* 验证码错误
*/
CAPTCHA_ERROR(RuleConstants.USER_OPERATION_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "09", "验证码错误");
/**
* 错误编码
*/
private final String errorCode;
/**
* 提示用户信息
*/
private final String userTip;
ValidatorExceptionEnum(String errorCode, String userTip) {
this.errorCode = errorCode;
this.userTip = userTip;
}
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 5、定义自定义异常,继承RuntimeException
/**
* 所有业务异常的基类
* <p>
* 在抛出异常时候,务必带上AbstractExceptionEnum枚举
* <p>
* 业务异常分为三种
* <p>
* 第一种是用户端操作的异常,例如用户输入参数为空,用户输入账号密码不正确
* <p>
* 第二种是当前系统业务逻辑出错,例如系统执行出错,磁盘空间不足
* <p>
* 第三种是第三方系统调用出错,例如文件服务调用失败,RPC调用超时
*
* @author fengshuonan
* @date 2020/10/15 9:07
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServiceException extends RuntimeException {
/**
* 错误码
*/
private String errorCode;
/**
* 返回给用户的提示信息
*/
private String userTip;
/**
* 异常的模块名称
*/
private String moduleName;
/**
* 根据模块名,错误码,用户提示直接抛出异常
*/
public ServiceException(String moduleName, String errorCode, String userTip) {
super(userTip);
this.errorCode = errorCode;
this.moduleName = moduleName;
this.userTip = userTip;
}
/**
* 如果要直接抛出ServiceException,可以用这个构造函数
*/
public ServiceException(String moduleName, AbstractExceptionEnum exception) {
super(exception.getUserTip());
this.moduleName = moduleName;
this.errorCode = exception.getErrorCode();
this.userTip = exception.getUserTip();
}
/**
* 不建议直接抛出ServiceException,因为这样无法确认是哪个模块抛出的异常
* <p>
* 建议使用业务异常时,都抛出自己模块的异常类
*/
public ServiceException(AbstractExceptionEnum exception) {
super(exception.getUserTip());
this.moduleName = RULE_MODULE_NAME;
this.errorCode = exception.getErrorCode();
this.userTip = exception.getUserTip();
}
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 全局异常处理器
/**
* 全局异常处理器,拦截控制器层的异常
*
* @author fengshuonan
* @date 2020/12/16 14:20
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 请求参数缺失异常
*
* @author fengshuonan
* @date 2020/12/16 14:20
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData missingParam(MissingServletRequestParameterException missingServletRequestParameterException) {
String parameterName = missingServletRequestParameterException.getParameterName();
String parameterType = missingServletRequestParameterException.getParameterType();
return renderJson(ValidatorExceptionEnum.MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION, parameterName, parameterType);
}
/**
* HttpMessageConverter转化异常,一般为json解析异常
*
* @author fengshuonan
* @date 2020/12/16 14:21
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData httpMessageNotReadable(HttpMessageNotReadableException httpMessageNotReadableException) {
log.error("参数格式传递异常,具体信息为:{}", httpMessageNotReadableException.getMessage());
return renderJson(ValidatorExceptionEnum.HTTP_MESSAGE_CONVERTER_ERROR);
}
/**
* 拦截不支持媒体类型异常
*
* @author fengshuonan
* @date 2020/12/16 14:26
*/
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData httpMediaTypeNotSupport(HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) {
log.error("参数格式传递异常,具体信息为:{}", httpMediaTypeNotSupportedException.getMessage());
return renderJson(ValidatorExceptionEnum.HTTP_MEDIA_TYPE_NOT_SUPPORT);
}
/**
* 不受支持的http method
*
* @author fengshuonan
* @date 2020/12/16 14:56
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData methodNotSupport(HttpServletRequest request) {
String httpMethod = request.getMethod().toUpperCase();
return renderJson(ValidatorExceptionEnum.HTTP_METHOD_NOT_SUPPORT, httpMethod);
}
/**
* 404找不到资源
*
* @author fengshuonan
* @date 2020/12/16 14:58
*/
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData notFound(NoHandlerFoundException e) {
return renderJson(ValidatorExceptionEnum.NOT_FOUND);
}
/**
* 请求参数校验失败,拦截 @Valid 校验失败的情况
*
* @author fengshuonan
* @date 2020/12/16 14:59
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData methodArgumentNotValidException(MethodArgumentNotValidException e) {
String bindingResult = getArgNotValidMessage(e.getBindingResult());
return renderJson(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR, bindingResult);
}
/**
* 请求参数校验失败,拦截 @Validated 校验失败的情况
* <p>
* 两个注解 @Valid 和 @Validated 区别是后者可以加分组校验,前者没有分组校验
*
* @author fengshuonan
* @date 2020/12/16 15:08
*/
@ExceptionHandler(BindException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData bindException(BindException e) {
String bindingResult = getArgNotValidMessage(e.getBindingResult());
return renderJson(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR, bindingResult);
}
/**
* 拦截 @TableUniqueValue 里抛出的异常
*
* @author fengshuonan
* @date 2020/12/26 14:05
*/
@ExceptionHandler(ValidationException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData bindException(ValidationException e) {
if (e.getCause() instanceof ParamValidateException) {
ParamValidateException paramValidateException = (ParamValidateException) e.getCause();
return renderJson(paramValidateException.getErrorCode(), paramValidateException.getUserTip());
}
return renderJson(e);
}
/**
* 拦截全校校验一类的异常
* <p>
* 这里重点做一类特殊处理,也就是对过期登录用用户
* <p>
* 如果用户登录过期,并且为ajax请求,则response的header增加session-timeout的标识
* <p>
* 如果用户登录过期,不是ajax请求,则直接跳转到登录页面,并提示会话超时
*
* @author fengshuonan
* @date 2020/12/16 15:11
*/
@ExceptionHandler(AuthException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String authError(AuthException authException, HttpServletRequest request, HttpServletResponse response, Model model) {
String errorCode = authException.getErrorCode();
// 如果是会话过期或超时
if (AuthExceptionEnum.AUTH_EXPIRED_ERROR.getErrorCode().equals(errorCode)) {
// 如果是普通请求
if (HttpServletUtil.getNormalRequestFlag(request)) {
model.addAttribute("tips", AuthExceptionEnum.AUTH_EXPIRED_ERROR.getUserTip());
return "/login.html";
} else {
// 其他请求或者是ajax请求
response.setHeader("Guns-Session-Timeout", "true");
ErrorResponseData errorResponseData = renderJson(authException.getErrorCode(), authException.getUserTip(), authException);
ResponseRenderUtil.renderJsonResponse(response, errorResponseData);
return null;
}
}
// 如果是没带token访问页面,则返回到登录界面
if (AuthExceptionEnum.TOKEN_GET_ERROR.getErrorCode().equals(errorCode)) {
if (HttpServletUtil.getNormalRequestFlag(request)) {
model.addAttribute("tips", AuthExceptionEnum.AUTH_EXPIRED_ERROR.getUserTip());
return "/login.html";
}
}
// 默认响应前端json
ErrorResponseData errorResponseData = renderJson(authException.getErrorCode(), authException.getUserTip(), authException);
ResponseRenderUtil.renderJsonResponse(response, errorResponseData);
return null;
}
/**
* 拦截业务代码抛出的异常
*
* @author fengshuonan
* @date 2020/12/16 15:11
*/
@ExceptionHandler(ServiceException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponseData businessError(ServiceException e) {
log.error("业务异常,具体信息为:{}", e.getMessage());
return renderJson(e.getErrorCode(), e.getUserTip(), e);
}
/**
* 拦截未知的运行时异常
*
* @author fengshuonan
* @date 2020/12/16 15:12
*/
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponseData serverError(Throwable e) {
log.error("服务器运行异常", e);
return renderJson(e);
}
/**
* 渲染异常json
*
* @author fengshuonan
* @date 2020/5/5 16:22
*/
private ErrorResponseData renderJson(String code, String message) {
return renderJson(code, message, null);
}
/**
* 渲染异常json
*
* @author fengshuonan
* @date 2020/5/5 16:22
*/
private ErrorResponseData renderJson(AbstractExceptionEnum exception, Object... params) {
return renderJson(exception.getErrorCode(), StrUtil.format(exception.getUserTip(), params), null);
}
/**
* 渲染异常json
*
* @author fengshuonan
* @date 2020/5/5 16:22
*/
private ErrorResponseData renderJson(AbstractExceptionEnum abstractExceptionEnum) {
return renderJson(abstractExceptionEnum.getErrorCode(), abstractExceptionEnum.getUserTip(), null);
}
/**
* 渲染异常json
*
* @author fengshuonan
* @date 2020/5/5 16:22
*/
private ErrorResponseData renderJson(Throwable throwable) {
return renderJson(DefaultBusinessExceptionEnum.SYSTEM_RUNTIME_ERROR.getErrorCode(), DefaultBusinessExceptionEnum.SYSTEM_RUNTIME_ERROR.getUserTip(), throwable);
}
/**
* 渲染异常json
* <p>
* 根据异常枚举和Throwable异常响应,异常信息响应堆栈第一行
*
* @author stylefeng
* @date 2020/5/5 16:22
*/
private ErrorResponseData renderJson(String code, String message, Throwable throwable) {
if (ObjectUtil.isNotNull(throwable)) {
ErrorResponseData errorResponseData = new ErrorResponseData(code, message);
ExceptionUtil.fillErrorResponseData(errorResponseData, throwable, ROOT_PACKAGE_NAME);
return errorResponseData;
} else {
return new ErrorResponseData(code, message);
}
}
/**
* 获取请求参数不正确的提示信息
* <p>
* 多个信息,拼接成用逗号分隔的形式
*
* @author stylefeng
* @date 2020/5/5 16:50
*/
private String getArgNotValidMessage(BindingResult bindingResult) {
if (bindingResult == null) {
return "";
}
StringBuilder stringBuilder = new StringBuilder();
//多个错误用逗号分隔
List<ObjectError> allErrorInfos = bindingResult.getAllErrors();
for (ObjectError error : allErrorInfos) {
stringBuilder.append(SymbolConstant.COMMA).append(error.getDefaultMessage());
}
//最终把首部的逗号去掉
return StrUtil.removePrefix(stringBuilder.toString(), SymbolConstant.COMMA);
}
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# Roses框架异常设计
作用:让业务更有条理性、系统更健壮
# ServiceException
/**
* 所有业务异常的基类
* <p>
* 在抛出异常时候,务必带上AbstractExceptionEnum枚举
* <p>
* 业务异常分为三种
* <p>
* 第一种是用户端操作的异常,例如用户输入参数为空,用户输入账号密码不正确
* <p>
* 第二种是当前系统业务逻辑出错,例如系统执行出错,磁盘空间不足
* <p>
* 第三种是第三方系统调用出错,例如文件服务调用失败,RPC调用超时
*
* @author fengshuonan
* @date 2020/10/15 9:07
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServiceException extends RuntimeException {
/**
* 错误码
*/
private String errorCode;
/**
* 返回给用户的提示信息
*/
private String userTip;
/**
* 异常的模块名称
*/
private String moduleName;
/**
* 根据模块名,错误码,用户提示直接抛出异常
*/
public ServiceException(String moduleName, String errorCode, String userTip) {
super(userTip);
this.errorCode = errorCode;
this.moduleName = moduleName;
this.userTip = userTip;
}
/**
* 如果要直接抛出ServiceException,可以用这个构造函数
*/
public ServiceException(String moduleName, AbstractExceptionEnum exception) {
super(exception.getUserTip());
this.moduleName = moduleName;
this.errorCode = exception.getErrorCode();
this.userTip = exception.getUserTip();
}
/**
* 不建议直接抛出ServiceException,因为这样无法确认是哪个模块抛出的异常
* <p>
* 建议使用业务异常时,都抛出自己模块的异常类
*/
public ServiceException(AbstractExceptionEnum exception) {
super(exception.getUserTip());
this.moduleName = RULE_MODULE_NAME;
this.errorCode = exception.getErrorCode();
this.userTip = exception.getUserTip();
}
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* 抛异常的时候,不打印堆栈信息,可以提高性能
* @return Throwable
*/
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
public Throwable doFillInStackTrace() {
return super.fillInStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12