18
18
19
19
import java .lang .reflect .Method ;
20
20
import java .util .ArrayList ;
21
+ import java .util .HashMap ;
22
+ import java .util .LinkedHashMap ;
21
23
import java .util .List ;
22
24
import java .util .Map ;
25
+ import java .util .Map .Entry ;
23
26
import java .util .concurrent .ConcurrentHashMap ;
24
27
25
28
import javax .servlet .http .HttpServletRequest ;
26
29
import javax .servlet .http .HttpServletResponse ;
27
30
import javax .xml .transform .Source ;
28
31
32
+ import org .springframework .beans .BeansException ;
29
33
import org .springframework .beans .factory .InitializingBean ;
34
+ import org .springframework .context .ApplicationContext ;
35
+ import org .springframework .context .ApplicationContextAware ;
36
+ import org .springframework .core .annotation .AnnotationAwareOrderComparator ;
37
+ import org .springframework .core .annotation .AnnotationUtils ;
30
38
import org .springframework .http .converter .ByteArrayHttpMessageConverter ;
31
39
import org .springframework .http .converter .HttpMessageConverter ;
32
40
import org .springframework .http .converter .StringHttpMessageConverter ;
33
41
import org .springframework .http .converter .xml .SourceHttpMessageConverter ;
34
42
import org .springframework .http .converter .xml .XmlAwareFormHttpMessageConverter ;
35
43
import org .springframework .web .accept .ContentNegotiationManager ;
36
44
import org .springframework .web .bind .annotation .ExceptionHandler ;
45
+ import org .springframework .web .bind .annotation .ExceptionResolver ;
37
46
import org .springframework .web .context .request .ServletWebRequest ;
38
47
import org .springframework .web .method .HandlerMethod ;
39
48
import org .springframework .web .method .annotation .ExceptionHandlerMethodResolver ;
49
58
import org .springframework .web .servlet .View ;
50
59
import org .springframework .web .servlet .handler .AbstractHandlerMethodExceptionResolver ;
51
60
61
+ import edu .emory .mathcs .backport .java .util .Collections ;
62
+
52
63
/**
53
64
* An {@link AbstractHandlerMethodExceptionResolver} that resolves exceptions
54
65
* through {@code @ExceptionHandler} methods.
62
73
* @since 3.1
63
74
*/
64
75
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver implements
65
- InitializingBean {
76
+ InitializingBean , ApplicationContextAware {
66
77
67
78
private List <HandlerMethodArgumentResolver > customArgumentResolvers ;
68
79
@@ -72,13 +83,18 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
72
83
73
84
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager ();
74
85
75
- private final Map <Class <?>, ExceptionHandlerMethodResolver > exceptionHandlerMethodResolvers =
76
- new ConcurrentHashMap <Class <?>, ExceptionHandlerMethodResolver >();
86
+ private final Map <Class <?>, ExceptionHandlerMethodResolver > exceptionHandlersByType =
87
+ new ConcurrentHashMap <Class <?>, ExceptionHandlerMethodResolver >();
88
+
89
+ private final Map <Object , ExceptionHandlerMethodResolver > globalExceptionHandlers =
90
+ new LinkedHashMap <Object , ExceptionHandlerMethodResolver >();
77
91
78
92
private HandlerMethodArgumentResolverComposite argumentResolvers ;
79
93
80
94
private HandlerMethodReturnValueHandlerComposite returnValueHandlers ;
81
95
96
+ private ApplicationContext applicationContext ;
97
+
82
98
/**
83
99
* Default constructor.
84
100
*/
@@ -193,6 +209,22 @@ public void setContentNegotiationManager(ContentNegotiationManager contentNegoti
193
209
this .contentNegotiationManager = contentNegotiationManager ;
194
210
}
195
211
212
+ /**
213
+ * Provide instances of objects with {@link ExceptionHandler @ExceptionHandler}
214
+ * methods to apply globally, i.e. regardless of the selected controller.
215
+ * <p>{@code @ExceptionHandler} methods in the controller are always looked
216
+ * up before {@code @ExceptionHandler} methods in global handlers.
217
+ */
218
+ public void setGlobalExceptionHandlers (Object ... handlers ) {
219
+ for (Object handler : handlers ) {
220
+ this .globalExceptionHandlers .put (handler , new ExceptionHandlerMethodResolver (handler .getClass ()));
221
+ }
222
+ }
223
+
224
+ public void setApplicationContext (ApplicationContext applicationContext ) throws BeansException {
225
+ this .applicationContext = applicationContext ;
226
+ }
227
+
196
228
public void afterPropertiesSet () {
197
229
if (this .argumentResolvers == null ) {
198
230
List <HandlerMethodArgumentResolver > resolvers = getDefaultArgumentResolvers ();
@@ -202,6 +234,7 @@ public void afterPropertiesSet() {
202
234
List <HandlerMethodReturnValueHandler > handlers = getDefaultReturnValueHandlers ();
203
235
this .returnValueHandlers = new HandlerMethodReturnValueHandlerComposite ().addHandlers (handlers );
204
236
}
237
+ initGlobalExceptionHandlers ();
205
238
}
206
239
207
240
/**
@@ -255,6 +288,36 @@ protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers()
255
288
return handlers ;
256
289
}
257
290
291
+ private void initGlobalExceptionHandlers () {
292
+ if (this .applicationContext == null ) {
293
+ logger .warn ("Can't detect @ExceptionResolver components if the ApplicationContext property is not set" );
294
+ }
295
+ else {
296
+ String [] beanNames = this .applicationContext .getBeanNamesForType (Object .class );
297
+ for (String name : beanNames ) {
298
+ Class <?> type = this .applicationContext .getType (name );
299
+ if (AnnotationUtils .findAnnotation (type , ExceptionResolver .class ) != null ) {
300
+ Object bean = this .applicationContext .getBean (name );
301
+ this .globalExceptionHandlers .put (bean , new ExceptionHandlerMethodResolver (bean .getClass ()));
302
+ }
303
+ }
304
+ }
305
+ if (this .globalExceptionHandlers .size () > 0 ) {
306
+ sortGlobalExceptionHandlers ();
307
+ }
308
+ }
309
+
310
+ private void sortGlobalExceptionHandlers () {
311
+ Map <Object , ExceptionHandlerMethodResolver > handlersCopy =
312
+ new HashMap <Object , ExceptionHandlerMethodResolver >(this .globalExceptionHandlers );
313
+ List <Object > handlers = new ArrayList <Object >(handlersCopy .keySet ());
314
+ Collections .sort (handlers , new AnnotationAwareOrderComparator ());
315
+ this .globalExceptionHandlers .clear ();
316
+ for (Object handler : handlers ) {
317
+ this .globalExceptionHandlers .put (handler , handlersCopy .get (handler ));
318
+ }
319
+ }
320
+
258
321
/**
259
322
* Find an @{@link ExceptionHandler} method and invoke it to handle the
260
323
* raised exception.
@@ -307,24 +370,32 @@ protected ModelAndView doResolveHandlerMethodException(HttpServletRequest reques
307
370
* @return a method to handle the exception, or {@code null}
308
371
*/
309
372
protected ServletInvocableHandlerMethod getExceptionHandlerMethod (HandlerMethod handlerMethod , Exception exception ) {
310
- if (handlerMethod == null ) {
311
- return null ;
373
+ if (handlerMethod != null ) {
374
+ Class <?> handlerType = handlerMethod .getBeanType ();
375
+ ExceptionHandlerMethodResolver resolver = this .exceptionHandlersByType .get (handlerType );
376
+ if (resolver == null ) {
377
+ resolver = new ExceptionHandlerMethodResolver (handlerType );
378
+ this .exceptionHandlersByType .put (handlerType , resolver );
379
+ }
380
+ Method method = resolver .resolveMethod (exception );
381
+ if (method != null ) {
382
+ return new ServletInvocableHandlerMethod (handlerMethod .getBean (), method );
383
+ }
312
384
}
313
- Class <?> handlerType = handlerMethod .getBeanType ();
314
- Method method = getExceptionHandlerMethodResolver (handlerType ).resolveMethod (exception );
315
- return (method != null ? new ServletInvocableHandlerMethod (handlerMethod .getBean (), method ) : null );
385
+ return getGlobalExceptionHandlerMethod (exception );
316
386
}
317
387
318
388
/**
319
- * Return a method resolver for the given handler type, never {@code null}.
389
+ * Return a global {@code @ExceptionHandler} method for the given exception or {@code null}.
320
390
*/
321
- private ExceptionHandlerMethodResolver getExceptionHandlerMethodResolver (Class <?> handlerType ) {
322
- ExceptionHandlerMethodResolver resolver = this .exceptionHandlerMethodResolvers .get (handlerType );
323
- if (resolver == null ) {
324
- resolver = new ExceptionHandlerMethodResolver (handlerType );
325
- this .exceptionHandlerMethodResolvers .put (handlerType , resolver );
391
+ private ServletInvocableHandlerMethod getGlobalExceptionHandlerMethod (Exception exception ) {
392
+ for (Entry <Object , ExceptionHandlerMethodResolver > entry : this .globalExceptionHandlers .entrySet ()) {
393
+ Method method = entry .getValue ().resolveMethod (exception );
394
+ if (method != null ) {
395
+ return new ServletInvocableHandlerMethod (entry .getKey (), method );
396
+ }
326
397
}
327
- return resolver ;
398
+ return null ;
328
399
}
329
400
330
401
}
0 commit comments