├── .gitignore
├── README.md
├── flylib-boot-demo
├── .gitignore
├── flylib-boot-demo.iml
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── org
│ │ └── flylib
│ │ ├── StarterApp.java
│ │ └── demo
│ │ ├── controller
│ │ ├── DemoController.java
│ │ └── IndexController.java
│ │ └── exception
│ │ └── UserException.java
│ └── resources
│ └── application.properties
├── flylib-boot-starter-web
├── .gitignore
├── flylib-boot-starter-web.iml
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── org
│ │ └── flylib
│ │ └── boot
│ │ └── starter
│ │ ├── StarterApp.java
│ │ ├── controller
│ │ ├── BasicErrorController.java
│ │ ├── DemoController.java
│ │ └── IndexController.java
│ │ ├── exception
│ │ ├── CustomRuntimeException.java
│ │ ├── UnknownResourceException.java
│ │ ├── UserException.java
│ │ └── ValidationRuntimeException.java
│ │ └── handler
│ │ └── GlobalExceptionHandler.java
│ └── resources
│ ├── application-dev.properties
│ └── application.properties
└── flylib-boot-starter
├── .gitignore
├── flylib-boot-starter.iml
├── pom.xml
└── src
└── main
└── java
└── org
└── flylib
└── boot
└── starter
├── config
└── MvcConfig.java
├── controller
└── DefaultController.java
├── exception
├── CustomRuntimeException.java
├── UnknownResourceException.java
└── ValidationRuntimeException.java
├── filter
└── LoggingFilter.java
├── handler
└── GlobalExceptionHandler.java
└── interceptor
└── ResponseHandlerInterceptor.java
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | /.classpath
4 | /.project
5 | /.idea/
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## flylib-boot简介
2 | flylib-boot是针对springboot构建的程序的基础框架,专门用于构建程序里的比如统一
3 | 异常处理
4 |
5 | ## 功能
6 | 包含一个Spring Boot的一些常见的基础组件的设置
7 | 1. 针对Handler的全局的异常处理(处理所有Controller里的Handler里抛出的异常)
8 | 2. Filter
9 | 3. Interceptor
10 |
11 | 注意:SpringBoot(SpringMVC)里的Handler特指@Controller注解的类里的每个处理HTTP请求的一个public method.
12 |
13 | ## 使用方法
14 | - Step 1: 进入目录flylib-boot-starter,执行mvn install
15 | - Step 2: 在自己的项目中添加flylib-boot-starter的maven依赖. 并留意自己使用的spring-boot版本,去修改自己的pom.xml文件
16 | ```xml
17 |
18 | org.flylib
19 | flylib-boot-starter
20 | 1.0-SNAPSHOT
21 |
22 | ```
23 | 并且要注意这里spring-boot版本是1.5.0.RELEASE. 另外需要添加spring-boot-maven-plugin
24 | 实例参考spring-boot-demo项目,它的pom如下:
25 | ```xml
26 |
27 |
29 | 4.0.0
30 |
31 | org.flylib
32 | flylib-boot-demo
33 | 1.0-SNAPSHOT
34 | jar
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-parent
39 | 1.5.0.RELEASE
40 |
41 |
42 |
43 |
44 | UTF-8
45 | UTF-8
46 | 1.8
47 |
48 |
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-starter-web
53 |
54 |
55 |
56 | org.springframework.boot
57 | spring-boot-starter-test
58 | test
59 |
60 |
61 | org.flylib
62 | flylib-boot-starter
63 | 1.0-SNAPSHOT
64 |
65 |
66 |
67 |
68 |
69 | org.springframework.boot
70 | spring-boot-maven-plugin
71 |
72 |
73 |
74 |
75 |
76 | ```
77 |
78 | - Step 3: 在自己的程序中new 一个UserException(自定义的异常类)设置捕获异常
79 | ```java
80 | /**
81 | * 用户信息的异常
82 | */
83 | public class UserException extends RuntimeException{
84 |
85 | }
86 |
87 |
88 | @RequestMapping("")
89 | public String index() throws RuntimeException {
90 | UserException userException = new UserException();
91 | CustomRuntimeException cause = new CustomRuntimeException("001", "User not exists");
92 | userException.initCause(cause);
93 | throw userException;
94 | }
95 | ```
96 | - Step 4: 运行自己的Spring Boot项目
97 | 输出到浏览器的结果
98 | ```json
99 | {
100 | code:"001",
101 | message:"User not exists",
102 | throwable:{...}
103 | }
104 | ```
105 | ## 实现原理
106 | 利用了@ControllerAdvice和@ExceptionHandler
107 | 实现代码是
108 | ```java
109 | package org.flylib.boot.starter.handler;
110 |
111 | import org.flylib.boot.starter.exception.CustomRuntimeException;
112 | import org.flylib.boot.starter.exception.UnknownResourceException;
113 | import org.flylib.boot.starter.exception.ValidationRuntimeException;
114 | import org.slf4j.Logger;
115 | import org.slf4j.LoggerFactory;
116 | import org.springframework.beans.TypeMismatchException;
117 | import org.springframework.beans.factory.annotation.Autowired;
118 | import org.springframework.context.MessageSource;
119 | import org.springframework.core.env.Environment;
120 | import org.springframework.http.HttpStatus;
121 | import org.springframework.http.converter.HttpMessageNotReadableException;
122 | import org.springframework.ui.Model;
123 | import org.springframework.web.HttpMediaTypeNotAcceptableException;
124 | import org.springframework.web.HttpMediaTypeNotSupportedException;
125 | import org.springframework.web.HttpRequestMethodNotSupportedException;
126 | import org.springframework.web.bind.MissingServletRequestParameterException;
127 | import org.springframework.web.bind.annotation.ControllerAdvice;
128 | import org.springframework.web.bind.annotation.ExceptionHandler;
129 | import org.springframework.web.bind.annotation.ResponseBody;
130 | import org.springframework.web.context.request.ServletWebRequest;
131 | import org.springframework.web.servlet.LocaleResolver;
132 | import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
133 |
134 | import javax.servlet.http.HttpServletRequest;
135 | import javax.servlet.http.HttpServletResponse;
136 | import java.util.HashMap;
137 | import java.util.LinkedHashMap;
138 | import java.util.Locale;
139 | import java.util.Map;
140 |
141 | /**
142 | * 说明:
143 | *
144 | * @ControllerAdvice是controller的一个辅助类,最常用的就是作为全局异常处理的切面类
145 | * @ControllerAdvice可以指定扫描范围
146 | * @ControllerAdvice约定了几种可行的返回值,如果是直接返回model类的话,需要使用@ResponseBody进行json转换 返回String,表示跳到某个view
147 | * 返回modelAndView
148 | * 返回model + @ResponseBody
149 | * 全局异常处理
150 | */
151 | @ControllerAdvice
152 | public class GlobalExceptionHandler {
153 |
154 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
155 |
156 | @Autowired
157 | private Environment env;
158 |
159 | @Autowired(required = false)
160 | private MessageSource messageSource;
161 |
162 | @Autowired(required = false)
163 | private LocaleResolver localeResolver;
164 |
165 | private static final String defaultMoreInfoUrl = "";
166 |
167 | private final Map DEFAULT_EXCEPTION_MAPPING_DEFINITIONS;
168 |
169 | public GlobalExceptionHandler() {
170 | DEFAULT_EXCEPTION_MAPPING_DEFINITIONS = createDefaultExceptionMappingDefinitions();
171 | }
172 |
173 | @ExceptionHandler//处理所有异常
174 | @ResponseBody //在返回自定义相应类的情况下必须有,这是@ControllerAdvice注解的规定
175 | public Map exceptionHandler(Throwable e, HttpServletRequest request, HttpServletResponse response, Model model) {
176 | //
177 | log.error("handle error:",e);
178 |
179 | HttpStatus httpStatus = DEFAULT_EXCEPTION_MAPPING_DEFINITIONS.get(e.getClass().getName());
180 | if(httpStatus==null){
181 | httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
182 | }
183 | //是否是生产环境
184 | boolean isProd = "prod".equals(env.getActiveProfiles()[0]);
185 | Map map = new HashMap();
186 | if(e.getCause() instanceof CustomRuntimeException){
187 | CustomRuntimeException exception = (CustomRuntimeException) e.getCause();
188 | map.put("code",String.valueOf(exception.getCode()));
189 | map.put("message",exception.getMessage());
190 | }else if(e.getCause() instanceof ValidationRuntimeException){
191 | ValidationRuntimeException exception = (ValidationRuntimeException) e.getCause();
192 | map.put("code",String.valueOf(exception.getCode()));
193 | map.put("message",exception.getMessage());
194 | httpStatus = HttpStatus.BAD_REQUEST;
195 | }else {
196 | map.put("code",String.valueOf(httpStatus.value()));
197 | map.put("message",httpStatus.toString());
198 | }
199 |
200 |
201 | //不是生产环境,添加调试信息
202 | if(!isProd){
203 | map.put("throwable",e);
204 | }
205 | response.setStatus(httpStatus.value());
206 | return map;
207 | }
208 |
209 | protected final Map createDefaultExceptionMappingDefinitions() {
210 |
211 | Map m = new LinkedHashMap();
212 |
213 | // 400
214 | applyDef(m, HttpMessageNotReadableException.class, HttpStatus.BAD_REQUEST);
215 | applyDef(m, MissingServletRequestParameterException.class, HttpStatus.BAD_REQUEST);
216 | applyDef(m, TypeMismatchException.class, HttpStatus.BAD_REQUEST);
217 | applyDef(m, "javax.validation.ValidationException", HttpStatus.BAD_REQUEST);
218 |
219 | // 404
220 | applyDef(m, NoSuchRequestHandlingMethodException.class, HttpStatus.NOT_FOUND);
221 | applyDef(m, "org.hibernate.ObjectNotFoundException", HttpStatus.NOT_FOUND);
222 |
223 | // 405
224 | applyDef(m, HttpRequestMethodNotSupportedException.class, HttpStatus.METHOD_NOT_ALLOWED);
225 |
226 | // 406
227 | applyDef(m, HttpMediaTypeNotAcceptableException.class, HttpStatus.NOT_ACCEPTABLE);
228 |
229 | // 409
230 | //can't use the class directly here as it may not be an available dependency:
231 | applyDef(m, "org.springframework.dao.DataIntegrityViolationException", HttpStatus.CONFLICT);
232 |
233 | // 415
234 | applyDef(m, HttpMediaTypeNotSupportedException.class, HttpStatus.UNSUPPORTED_MEDIA_TYPE);
235 | applyDef(m, UnknownResourceException.class, HttpStatus.NOT_FOUND);
236 |
237 | return m;
238 | }
239 | private void applyDef(Map m, Class clazz, HttpStatus status) {
240 | applyDef(m, clazz.getName(), status);
241 | }
242 |
243 | private void applyDef(Map m, String key, HttpStatus status) {
244 | m.put(key, status);
245 | }
246 |
247 |
248 |
249 | protected String getMessage(String msg, ServletWebRequest webRequest, Exception ex) {
250 |
251 | if (msg != null) {
252 | if (msg.equalsIgnoreCase("null") || msg.equalsIgnoreCase("off")) {
253 | return null;
254 | }
255 | msg = ex.getMessage();
256 | if (messageSource != null) {
257 | Locale locale = null;
258 | if (localeResolver != null) {
259 | locale = localeResolver.resolveLocale(webRequest.getRequest());
260 | }
261 | msg = messageSource.getMessage(msg, null, msg, locale);
262 | }
263 | }
264 |
265 | return msg;
266 | }
267 | }
268 |
269 | ```
270 |
--------------------------------------------------------------------------------
/flylib-boot-demo/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | /.classpath
4 | /.project
5 | /.idea/
6 |
--------------------------------------------------------------------------------
/flylib-boot-demo/flylib-boot-demo.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/flylib-boot-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.flylib
7 | flylib-boot-demo
8 | 1.0-SNAPSHOT
9 | jar
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 1.5.0.RELEASE
15 |
16 |
17 |
18 |
19 | UTF-8
20 | UTF-8
21 | 1.8
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-web
28 |
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-test
33 | test
34 |
35 |
36 | org.flylib
37 | flylib-boot-starter
38 | 1.0-SNAPSHOT
39 |
40 |
41 |
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-maven-plugin
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/flylib-boot-demo/src/main/java/org/flylib/StarterApp.java:
--------------------------------------------------------------------------------
1 | package org.flylib;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class StarterApp {
8 | public static void main(String[] args) {
9 | SpringApplication.run(StarterApp.class, args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/flylib-boot-demo/src/main/java/org/flylib/demo/controller/DemoController.java:
--------------------------------------------------------------------------------
1 | package org.flylib.demo.controller;
2 |
3 | import org.flylib.boot.starter.exception.CustomRuntimeException;
4 | import org.flylib.demo.exception.UserException;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | @RestController
9 | @RequestMapping("demo")
10 | public class DemoController {
11 |
12 | @RequestMapping("")
13 | public String index() throws RuntimeException {
14 | UserException userException = new UserException();
15 | CustomRuntimeException cause = new CustomRuntimeException("001", "User not exists");
16 | userException.initCause(cause);
17 | throw userException;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/flylib-boot-demo/src/main/java/org/flylib/demo/controller/IndexController.java:
--------------------------------------------------------------------------------
1 | package org.flylib.demo.controller;
2 |
3 | import org.springframework.web.bind.annotation.RequestMapping;
4 | import org.springframework.web.bind.annotation.RestController;
5 |
6 | @RestController
7 | @RequestMapping("")
8 | public class IndexController {
9 |
10 | @RequestMapping("")
11 | public String index() {
12 | return "hello";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/flylib-boot-demo/src/main/java/org/flylib/demo/exception/UserException.java:
--------------------------------------------------------------------------------
1 | package org.flylib.demo.exception;
2 |
3 | /**
4 | * 用户信息的异常
5 | */
6 | public class UserException extends RuntimeException{
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/flylib-boot-demo/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.profiles.active=dev
2 | server.port=3000
3 | #server.error.path=/custom/error
--------------------------------------------------------------------------------
/flylib-boot-starter-web/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | /.classpath
4 | /.project
5 | /.idea/
6 |
--------------------------------------------------------------------------------
/flylib-boot-starter-web/flylib-boot-starter-web.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/flylib-boot-starter-web/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.springframework.boot
8 | spring-boot-starter-parent
9 | 1.5.8.RELEASE
10 |
11 |
12 | org.flylib
13 | flylib-boot-starter-web
14 | 1.0-SNAPSHOT
15 | jar
16 |
17 |
18 | UTF-8
19 | UTF-8
20 | 1.8
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-web
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-test
32 | test
33 |
34 |
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-maven-plugin
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/flylib-boot-starter-web/src/main/java/org/flylib/boot/starter/StarterApp.java:
--------------------------------------------------------------------------------
1 | package org.flylib.boot.starter;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class StarterApp {
8 | public static void main(String[] args) {
9 | SpringApplication.run(StarterApp.class, args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/flylib-boot-starter-web/src/main/java/org/flylib/boot/starter/controller/BasicErrorController.java:
--------------------------------------------------------------------------------
1 | //
2 | // Source code recreated from a .class file by IntelliJ IDEA
3 | // (powered by Fernflower decompiler)
4 | //
5 |
6 | package org.flylib.boot.starter.controller;
7 |
8 | import java.util.Collections;
9 | import java.util.HashMap;
10 | import java.util.Iterator;
11 | import java.util.List;
12 | import java.util.Map;
13 | import javax.servlet.http.HttpServletRequest;
14 | import javax.servlet.http.HttpServletResponse;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.boot.autoconfigure.web.ErrorAttributes;
17 | import org.springframework.boot.autoconfigure.web.ErrorController;
18 | import org.springframework.boot.autoconfigure.web.ErrorProperties;
19 | import org.springframework.boot.autoconfigure.web.ErrorViewResolver;
20 | import org.springframework.boot.autoconfigure.web.ServerProperties;
21 | import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
22 | import org.springframework.core.env.Environment;
23 | import org.springframework.http.HttpStatus;
24 | import org.springframework.http.MediaType;
25 | import org.springframework.http.ResponseEntity;
26 | import org.springframework.stereotype.Controller;
27 | import org.springframework.web.bind.annotation.RequestMapping;
28 | import org.springframework.web.bind.annotation.ResponseBody;
29 | import org.springframework.web.context.request.RequestAttributes;
30 | import org.springframework.web.context.request.ServletRequestAttributes;
31 | import org.springframework.web.servlet.ModelAndView;
32 |
33 | @Controller
34 | @RequestMapping({"${server.error.path:${error.path:/error}}"})
35 | public class BasicErrorController implements ErrorController {
36 | @Autowired
37 | private ServerProperties serverProperties;
38 | @Autowired
39 | private Environment env;
40 | @Autowired
41 | private ErrorAttributes errorAttributes;
42 | @Autowired(
43 | required = false
44 | )
45 | private List errorViewResolvers;
46 |
47 | public BasicErrorController() {
48 | }
49 |
50 | public String getErrorPath() {
51 | return this.serverProperties.getError().getPath();
52 | }
53 |
54 | @RequestMapping(
55 | produces = {"text/html"}
56 | )
57 | public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
58 | HttpStatus status = this.getStatus(request);
59 | Map model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
60 | response.setStatus(status.value());
61 | request.setAttribute("env", this.env.getActiveProfiles()[0]);
62 | ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
63 | return modelAndView == null ? new ModelAndView("error", model) : modelAndView;
64 | }
65 |
66 | @RequestMapping
67 | @ResponseBody
68 | public ResponseEntity