22
33import com .example .demo .common .api .response .ServiceResponse ;
44import java .lang .reflect .Method ;
5- import java .util .List ;
6- import java .util .Objects ;
75import java .util .Optional ;
6+ import java .util .Set ;
7+ import java .util .concurrent .CompletionStage ;
8+ import java .util .concurrent .Future ;
9+ import org .slf4j .Logger ;
10+ import org .slf4j .LoggerFactory ;
811import org .springframework .core .ResolvableType ;
912import org .springframework .http .ResponseEntity ;
1013import org .springframework .stereotype .Component ;
14+ import org .springframework .web .context .request .async .DeferredResult ;
15+ import org .springframework .web .context .request .async .WebAsyncTask ;
1116
1217@ Component
13- public class ResponseTypeIntrospector {
18+ public final class ResponseTypeIntrospector {
1419
15- private static final List <String > ASYNC_WRAPPERS =
16- List .of (
17- "reactor.core.publisher.Mono" ,
18- "reactor.core.publisher.Flux" ,
19- "java.util.concurrent.CompletionStage" ,
20- "java.util.concurrent.Future" ,
21- "org.springframework.web.context.request.async.DeferredResult" ,
22- "org.springframework.web.context.request.async.WebAsyncTask" );
20+ private static final Logger log = LoggerFactory .getLogger (ResponseTypeIntrospector .class );
21+
22+ private static final int MAX_UNWRAP_DEPTH = 8 ;
23+
24+ private static final Set <String > REACTOR_WRAPPERS =
25+ Set .of ("reactor.core.publisher.Mono" , "reactor.core.publisher.Flux" );
2326
2427 public Optional <String > extractDataRefName (Method method ) {
2528 if (method == null ) return Optional .empty ();
2629
27- ResolvableType rt = ResolvableType .forMethodReturnType (method );
28-
29- rt = unwrapIf (rt );
30+ ResolvableType type = ResolvableType .forMethodReturnType (method );
31+ type = unwrapToServiceResponse (type );
3032
31- for ( String wrapper : ASYNC_WRAPPERS ) {
32- rt = unwrapIf ( rt , wrapper );
33- }
33+ Class <?> raw = type . resolve ();
34+ if ( raw == null || ! ServiceResponse . class . isAssignableFrom ( raw )) return Optional . empty ( );
35+ if (! type . hasGenerics ()) return Optional . empty ();
3436
35- Class <?> raw = rt .resolve ();
36- if (raw == null || !ServiceResponse .class .isAssignableFrom (raw )) {
37- return Optional .empty ();
38- }
37+ Class <?> dataClass = type .getGeneric (0 ).resolve ();
38+ Optional <String > ref = Optional .ofNullable (dataClass ).map (Class ::getSimpleName );
3939
40- if (rt .getGenerics ().length == 0 ) {
41- return Optional .empty ();
40+ if (log .isDebugEnabled ()) {
41+ log .debug ("Introspected method [{}]: wrapper [{}], data [{}]" ,
42+ method .toGenericString (), raw .getSimpleName (), ref .orElse ("<none>" ));
4243 }
4344
44- Class <?> dataClass = rt .getGeneric (0 ).resolve ();
45- return Optional .ofNullable (dataClass ).map (Class ::getSimpleName );
45+ return ref ;
4646 }
4747
48- private ResolvableType unwrapIf (ResolvableType type ) {
49- Class <?> raw = type .resolve ();
50- if (raw != null && ResponseEntity .class .isAssignableFrom (raw )) {
51- return type .getGeneric (0 );
48+ private ResolvableType unwrapToServiceResponse (ResolvableType type ) {
49+ for (int guard = 0 ; guard < MAX_UNWRAP_DEPTH ; guard ++) {
50+ Class <?> raw = type .resolve ();
51+ if (raw == null || ServiceResponse .class .isAssignableFrom (raw )) {
52+ return type ;
53+ }
54+ ResolvableType next = nextLayer (type , raw );
55+ if (next == null ) {
56+ return type ;
57+ }
58+ type = next ;
5259 }
5360 return type ;
5461 }
5562
56- private ResolvableType unwrapIf (ResolvableType type , String wrapperClassName ) {
57- Class <?> raw = type .resolve ();
58- if (raw != null && Objects .equals (raw .getName (), wrapperClassName )) {
59- return type .getGeneric (0 );
60- }
61- return type ;
63+ private ResolvableType nextLayer (ResolvableType current , Class <?> raw ) {
64+ return switch (raw ) {
65+ case Class <?> c when ResponseEntity .class .isAssignableFrom (c ) -> current .getGeneric (0 );
66+ case Class <?> c when CompletionStage .class .isAssignableFrom (c )
67+ || Future .class .isAssignableFrom (c ) -> current .getGeneric (0 );
68+ case Class <?> c when DeferredResult .class .isAssignableFrom (c )
69+ || WebAsyncTask .class .isAssignableFrom (c ) -> current .getGeneric (0 );
70+ case Class <?> c when REACTOR_WRAPPERS .contains (c .getName ()) -> current .getGeneric (0 );
71+ default -> null ;
72+ };
6273 }
63- }
74+ }
0 commit comments