6161import org .slf4j .Logger ;
6262import org .slf4j .LoggerFactory ;
6363import org .springdoc .core .models .ControllerAdviceInfo ;
64+ import org .springdoc .core .models .MethodAdviceInfo ;
6465import org .springdoc .core .models .MethodAttributes ;
6566import org .springdoc .core .parsers .ReturnTypeParser ;
6667import org .springdoc .core .properties .SpringDocConfigProperties ;
@@ -242,7 +243,7 @@ public static void setResponseEntityExceptionHandlerClass(Class<?> responseEntit
242243 */
243244 public ApiResponses build (Components components , HandlerMethod handlerMethod , Operation operation ,
244245 MethodAttributes methodAttributes ) {
245- Map <String , ApiResponse > genericMapResponse = getGenericMapResponse (handlerMethod . getBeanType () );
246+ Map <String , ApiResponse > genericMapResponse = getGenericMapResponse (handlerMethod );
246247 if (springDocConfigProperties .isOverrideWithGenericResponse ()) {
247248 genericMapResponse = filterAndEnrichGenericMapResponseByDeclarations (handlerMethod , genericMapResponse );
248249 }
@@ -316,8 +317,13 @@ public void buildGenericResponse(Components components, Map<String, Object> find
316317 String [] methodProduces = { springDocConfigProperties .getDefaultProducesMediaType () };
317318 if (reqMappingMethod != null )
318319 methodProduces = reqMappingMethod .produces ();
319- Map <String , ApiResponse > controllerAdviceInfoApiResponseMap = controllerAdviceInfo .getApiResponseMap ();
320320 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 ();
321327 ApiResponses apiResponsesOp = new ApiResponses ();
322328 MethodAttributes methodAttributes = new MethodAttributes (methodProduces , springDocConfigProperties .getDefaultConsumesMediaType (),
323329 springDocConfigProperties .getDefaultProducesMediaType (), controllerAdviceInfoApiResponseMap , locale );
@@ -328,9 +334,9 @@ public void buildGenericResponse(Components components, Map<String, Object> find
328334 JavadocProvider javadocProvider = operationService .getJavadocProvider ();
329335 methodAttributes .setJavadocReturn (javadocProvider .getMethodJavadocReturn (methodParameter .getMethod ()));
330336 }
331- Map < String , ApiResponse > apiResponses = computeResponseFromDoc (components , methodParameter , apiResponsesOp , methodAttributes , springDocConfigProperties .isOpenapi31 (), locale );
337+ computeResponseFromDoc (components , methodParameter , apiResponsesOp , methodAttributes , springDocConfigProperties .isOpenapi31 (), locale );
332338 buildGenericApiResponses (components , methodParameter , apiResponsesOp , methodAttributes );
333- apiResponses . forEach ( controllerAdviceInfoApiResponseMap :: put );
339+ methodAdviceInfo . setApiResponses ( apiResponsesOp );
334340 }
335341 }
336342 if (AnnotatedElementUtils .hasAnnotation (objClz , ControllerAdvice .class )) {
@@ -382,7 +388,7 @@ private Map<String, ApiResponse> computeResponseFromDoc(Components components, M
382388 apiResponse .setDescription (propertyResolverUtils .resolve (apiResponseAnnotations .description (), methodAttributes .getLocale ()));
383389 buildContentFromDoc (components , apiResponsesOp , methodAttributes , apiResponseAnnotations , apiResponse , openapi31 );
384390 Map <String , Object > extensions = AnnotationsUtils .getExtensions (propertyResolverUtils .isOpenapi31 (), apiResponseAnnotations .extensions ());
385- if (!CollectionUtils .isEmpty (extensions )){
391+ if (!CollectionUtils .isEmpty (extensions )) {
386392 if (propertyResolverUtils .isResolveExtensionsProperties ()) {
387393 Map <String , Object > extensionsResolved = propertyResolverUtils .resolveExtensions (locale , extensions );
388394 extensionsResolved .forEach (apiResponse ::addExtension );
@@ -627,18 +633,7 @@ else if (CollectionUtils.isEmpty(apiResponse.getContent()))
627633 && methodParameter .getExecutable ().isAnnotationPresent (ExceptionHandler .class )) {
628634 // ExceptionHandler's exception class resolution is non-trivial
629635 // 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 );
642637 apiResponse .addExtension (EXTENSION_EXCEPTION_CLASSES , exceptions );
643638 }
644639 apiResponsesOp .addApiResponse (httpCode , apiResponse );
@@ -685,20 +680,21 @@ else if (returnType instanceof ParameterizedType) {
685680 /**
686681 * Gets generic map response.
687682 *
688- * @param beanType the bean type
683+ * @param handlerMethod the handler method
689684 * @return the generic map response
690685 */
691- private Map <String , ApiResponse > getGenericMapResponse (Class <?> beanType ) {
686+ private Map <String , ApiResponse > getGenericMapResponse (HandlerMethod handlerMethod ) {
692687 reentrantLock .lock ();
693688 try {
689+ Class <?> beanType = handlerMethod .getBeanType ();
694690 List <ControllerAdviceInfo > controllerAdviceInfosInThisBean = localExceptionHandlers .stream ()
695691 .filter (controllerInfo -> {
696692 Class <?> objClz = controllerInfo .getControllerAdvice ().getClass ();
697693 if (org .springframework .aop .support .AopUtils .isAopProxy (controllerInfo .getControllerAdvice ()))
698694 objClz = org .springframework .aop .support .AopUtils .getTargetClass (controllerInfo .getControllerAdvice ());
699695 return beanType .equals (objClz );
700696 })
701- .collect ( Collectors . toList () );
697+ .toList ();
702698
703699 Map <String , ApiResponse > genericApiResponseMap = controllerAdviceInfosInThisBean .stream ()
704700 .map (ControllerAdviceInfo ::getApiResponseMap )
@@ -710,11 +706,32 @@ private Map<String, ApiResponse> getGenericMapResponse(Class<?> beanType) {
710706 .filter (controllerAdviceInfo -> !beanType .equals (controllerAdviceInfo .getControllerAdvice ().getClass ()))
711707 .toList ();
712708
709+ Class <?>[] methodExceptions = handlerMethod .getMethod ().getExceptionTypes ();
710+
713711 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+ }
718735 }
719736
720737 LinkedHashMap <String , ApiResponse > genericApiResponsesClone ;
@@ -732,7 +749,7 @@ private Map<String, ApiResponse> getGenericMapResponse(Class<?> beanType) {
732749 reentrantLock .unlock ();
733750 }
734751 }
735-
752+
736753 /**
737754 * Is valid http code boolean.
738755 *
@@ -773,4 +790,40 @@ private boolean isHttpCodePresent(String httpCode, Set<io.swagger.v3.oas.annotat
773790 return !responseSet .isEmpty () && responseSet .stream ().anyMatch (apiResponseAnnotations -> httpCode .equals (apiResponseAnnotations .responseCode ()));
774791 }
775792
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+ }
776829}
0 commit comments