61
61
import org .slf4j .Logger ;
62
62
import org .slf4j .LoggerFactory ;
63
63
import org .springdoc .core .models .ControllerAdviceInfo ;
64
+ import org .springdoc .core .models .MethodAdviceInfo ;
64
65
import org .springdoc .core .models .MethodAttributes ;
65
66
import org .springdoc .core .parsers .ReturnTypeParser ;
66
67
import org .springdoc .core .properties .SpringDocConfigProperties ;
@@ -242,7 +243,7 @@ public static void setResponseEntityExceptionHandlerClass(Class<?> responseEntit
242
243
*/
243
244
public ApiResponses build (Components components , HandlerMethod handlerMethod , Operation operation ,
244
245
MethodAttributes methodAttributes ) {
245
- Map <String , ApiResponse > genericMapResponse = getGenericMapResponse (handlerMethod . getBeanType () );
246
+ Map <String , ApiResponse > genericMapResponse = getGenericMapResponse (handlerMethod );
246
247
if (springDocConfigProperties .isOverrideWithGenericResponse ()) {
247
248
genericMapResponse = filterAndEnrichGenericMapResponseByDeclarations (handlerMethod , genericMapResponse );
248
249
}
@@ -316,8 +317,13 @@ public void buildGenericResponse(Components components, Map<String, Object> find
316
317
String [] methodProduces = { springDocConfigProperties .getDefaultProducesMediaType () };
317
318
if (reqMappingMethod != null )
318
319
methodProduces = reqMappingMethod .produces ();
319
- Map <String , ApiResponse > controllerAdviceInfoApiResponseMap = controllerAdviceInfo .getApiResponseMap ();
320
320
MethodParameter methodParameter = new MethodParameter (method , -1 );
321
+ MethodAdviceInfo methodAdviceInfo = new MethodAdviceInfo (method );
322
+ controllerAdviceInfo .addMethodAdviceInfos (methodAdviceInfo );
323
+ // get exceptions lists
324
+ Set <Class <?>> exceptions = getExceptionsFromExceptionHandler (methodParameter );
325
+ methodAdviceInfo .setExceptions (exceptions );
326
+ Map <String , ApiResponse > controllerAdviceInfoApiResponseMap = controllerAdviceInfo .getApiResponseMap ();
321
327
ApiResponses apiResponsesOp = new ApiResponses ();
322
328
MethodAttributes methodAttributes = new MethodAttributes (methodProduces , springDocConfigProperties .getDefaultConsumesMediaType (),
323
329
springDocConfigProperties .getDefaultProducesMediaType (), controllerAdviceInfoApiResponseMap , locale );
@@ -328,9 +334,9 @@ public void buildGenericResponse(Components components, Map<String, Object> find
328
334
JavadocProvider javadocProvider = operationService .getJavadocProvider ();
329
335
methodAttributes .setJavadocReturn (javadocProvider .getMethodJavadocReturn (methodParameter .getMethod ()));
330
336
}
331
- Map < String , ApiResponse > apiResponses = computeResponseFromDoc (components , methodParameter , apiResponsesOp , methodAttributes , springDocConfigProperties .isOpenapi31 (), locale );
337
+ computeResponseFromDoc (components , methodParameter , apiResponsesOp , methodAttributes , springDocConfigProperties .isOpenapi31 (), locale );
332
338
buildGenericApiResponses (components , methodParameter , apiResponsesOp , methodAttributes );
333
- apiResponses . forEach ( controllerAdviceInfoApiResponseMap :: put );
339
+ methodAdviceInfo . setApiResponses ( apiResponsesOp );
334
340
}
335
341
}
336
342
if (AnnotatedElementUtils .hasAnnotation (objClz , ControllerAdvice .class )) {
@@ -382,7 +388,7 @@ private Map<String, ApiResponse> computeResponseFromDoc(Components components, M
382
388
apiResponse .setDescription (propertyResolverUtils .resolve (apiResponseAnnotations .description (), methodAttributes .getLocale ()));
383
389
buildContentFromDoc (components , apiResponsesOp , methodAttributes , apiResponseAnnotations , apiResponse , openapi31 );
384
390
Map <String , Object > extensions = AnnotationsUtils .getExtensions (propertyResolverUtils .isOpenapi31 (), apiResponseAnnotations .extensions ());
385
- if (!CollectionUtils .isEmpty (extensions )){
391
+ if (!CollectionUtils .isEmpty (extensions )) {
386
392
if (propertyResolverUtils .isResolveExtensionsProperties ()) {
387
393
Map <String , Object > extensionsResolved = propertyResolverUtils .resolveExtensions (locale , extensions );
388
394
extensionsResolved .forEach (apiResponse ::addExtension );
@@ -627,18 +633,7 @@ else if (CollectionUtils.isEmpty(apiResponse.getContent()))
627
633
&& methodParameter .getExecutable ().isAnnotationPresent (ExceptionHandler .class )) {
628
634
// ExceptionHandler's exception class resolution is non-trivial
629
635
// more info on its javadoc
630
- ExceptionHandler exceptionHandler = methodParameter .getExecutable ().getAnnotation (ExceptionHandler .class );
631
- Set <Class <?>> exceptions = new HashSet <>();
632
- if (exceptionHandler .value ().length == 0 ) {
633
- for (Parameter parameter : methodParameter .getExecutable ().getParameters ()) {
634
- if (Throwable .class .isAssignableFrom (parameter .getType ())) {
635
- exceptions .add (parameter .getType ());
636
- }
637
- }
638
- }
639
- else {
640
- exceptions .addAll (asList (exceptionHandler .value ()));
641
- }
636
+ Set <Class <?>> exceptions = getExceptionsFromExceptionHandler (methodParameter );
642
637
apiResponse .addExtension (EXTENSION_EXCEPTION_CLASSES , exceptions );
643
638
}
644
639
apiResponsesOp .addApiResponse (httpCode , apiResponse );
@@ -685,20 +680,21 @@ else if (returnType instanceof ParameterizedType) {
685
680
/**
686
681
* Gets generic map response.
687
682
*
688
- * @param beanType the bean type
683
+ * @param handlerMethod the handler method
689
684
* @return the generic map response
690
685
*/
691
- private Map <String , ApiResponse > getGenericMapResponse (Class <?> beanType ) {
686
+ private Map <String , ApiResponse > getGenericMapResponse (HandlerMethod handlerMethod ) {
692
687
reentrantLock .lock ();
693
688
try {
689
+ Class <?> beanType = handlerMethod .getBeanType ();
694
690
List <ControllerAdviceInfo > controllerAdviceInfosInThisBean = localExceptionHandlers .stream ()
695
691
.filter (controllerInfo -> {
696
692
Class <?> objClz = controllerInfo .getControllerAdvice ().getClass ();
697
693
if (org .springframework .aop .support .AopUtils .isAopProxy (controllerInfo .getControllerAdvice ()))
698
694
objClz = org .springframework .aop .support .AopUtils .getTargetClass (controllerInfo .getControllerAdvice ());
699
695
return beanType .equals (objClz );
700
696
})
701
- .collect ( Collectors . toList () );
697
+ .toList ();
702
698
703
699
Map <String , ApiResponse > genericApiResponseMap = controllerAdviceInfosInThisBean .stream ()
704
700
.map (ControllerAdviceInfo ::getApiResponseMap )
@@ -710,11 +706,32 @@ private Map<String, ApiResponse> getGenericMapResponse(Class<?> beanType) {
710
706
.filter (controllerAdviceInfo -> !beanType .equals (controllerAdviceInfo .getControllerAdvice ().getClass ()))
711
707
.toList ();
712
708
709
+ Class <?>[] methodExceptions = handlerMethod .getMethod ().getExceptionTypes ();
710
+
713
711
for (ControllerAdviceInfo controllerAdviceInfo : controllerAdviceInfosNotInThisBean ) {
714
- controllerAdviceInfo .getApiResponseMap ().forEach ((key , apiResponse ) -> {
715
- if (!genericApiResponseMap .containsKey (key ))
716
- genericApiResponseMap .put (key , apiResponse );
717
- });
712
+ List <MethodAdviceInfo > methodAdviceInfos = controllerAdviceInfo .getMethodAdviceInfos ();
713
+ for (MethodAdviceInfo methodAdviceInfo : methodAdviceInfos ) {
714
+ Set <Class <?>> exceptions = methodAdviceInfo .getExceptions ();
715
+ boolean addToGenericMap = false ;
716
+
717
+ for (Class <?> exception : exceptions ) {
718
+ if (isGlobalException (exception ) ||
719
+ Arrays .stream (methodExceptions ).anyMatch (methodException ->
720
+ methodException .isAssignableFrom (exception ) ||
721
+ exception .isAssignableFrom (methodException ))) {
722
+
723
+ addToGenericMap = true ;
724
+ break ;
725
+ }
726
+ }
727
+
728
+ if (addToGenericMap || exceptions .isEmpty ()) {
729
+ methodAdviceInfo .getApiResponses ().forEach ((key , apiResponse ) -> {
730
+ if (!genericApiResponseMap .containsKey (key ))
731
+ genericApiResponseMap .put (key , apiResponse );
732
+ });
733
+ }
734
+ }
718
735
}
719
736
720
737
LinkedHashMap <String , ApiResponse > genericApiResponsesClone ;
@@ -732,7 +749,7 @@ private Map<String, ApiResponse> getGenericMapResponse(Class<?> beanType) {
732
749
reentrantLock .unlock ();
733
750
}
734
751
}
735
-
752
+
736
753
/**
737
754
* Is valid http code boolean.
738
755
*
@@ -773,4 +790,40 @@ private boolean isHttpCodePresent(String httpCode, Set<io.swagger.v3.oas.annotat
773
790
return !responseSet .isEmpty () && responseSet .stream ().anyMatch (apiResponseAnnotations -> httpCode .equals (apiResponseAnnotations .responseCode ()));
774
791
}
775
792
793
+ /**
794
+ * Gets exceptions from exception handler.
795
+ *
796
+ * @param methodParameter the method parameter
797
+ * @return the exceptions from exception handler
798
+ */
799
+ private Set <Class <?>> getExceptionsFromExceptionHandler (MethodParameter methodParameter ) {
800
+ ExceptionHandler exceptionHandler = methodParameter .getExecutable ().getAnnotation (ExceptionHandler .class );
801
+ Set <Class <?>> exceptions = new HashSet <>();
802
+ if (exceptionHandler != null ) {
803
+ if (exceptionHandler .value ().length == 0 ) {
804
+ for (Parameter parameter : methodParameter .getExecutable ().getParameters ()) {
805
+ if (Throwable .class .isAssignableFrom (parameter .getType ())) {
806
+ exceptions .add (parameter .getType ());
807
+ }
808
+ }
809
+ }
810
+ else {
811
+ exceptions .addAll (asList (exceptionHandler .value ()));
812
+ }
813
+ }
814
+ return exceptions ;
815
+ }
816
+
817
+
818
+ /**
819
+ * Is unchecked exception boolean.
820
+ *
821
+ * @param exceptionClass the exception class
822
+ * @return the boolean
823
+ */
824
+ private boolean isGlobalException (Class <?> exceptionClass ) {
825
+ return RuntimeException .class .isAssignableFrom (exceptionClass )
826
+ || exceptionClass .isAssignableFrom (Exception .class )
827
+ || Error .class .isAssignableFrom (exceptionClass );
828
+ }
776
829
}
0 commit comments