Skip to content
This repository was archived by the owner on Dec 4, 2025. It is now read-only.

Commit 8dff300

Browse files
authored
Merge pull request #825 from linwumingshi/fix/entry-annotations-extend
fix: 🐛 Prevent EntryAnnotation from being inherited from parent classes and interfaces
2 parents 263cbb5 + 25647a1 commit 8dff300

2 files changed

Lines changed: 157 additions & 20 deletions

File tree

src/main/java/com/ly/doc/model/annotation/FrameworkAnnotations.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,63 @@
2323
import java.util.Map;
2424

2525
/**
26+
* A model class representing various framework annotations.
27+
* Provides getter and setter methods to access and modify the annotations.
28+
* <p>
29+
* This class includes annotations for entry points, headers, mappings, path variables,
30+
* request parameters, request bodies, request parts, exception advice, and server endpoints.
31+
* </p>
32+
*
2633
* @author yu3.sun on 2022/10/1
2734
*/
2835
public class FrameworkAnnotations {
2936

37+
/**
38+
* Map of entry annotations.
39+
*/
3040
private Map<String, EntryAnnotation> entryAnnotations;
3141

42+
/**
43+
* Header annotation.
44+
*/
3245
private HeaderAnnotation headerAnnotation;
3346

47+
/**
48+
* Map of mapping annotations.
49+
*/
3450
private Map<String, MappingAnnotation> mappingAnnotations;
3551

52+
/**
53+
* Path variable annotation.
54+
*/
3655
private PathVariableAnnotation pathVariableAnnotation;
3756

57+
/**
58+
* Request parameter annotation.
59+
*/
3860
private RequestParamAnnotation requestParamAnnotation;
3961

62+
/**
63+
* Request body annotation.
64+
*/
4065
private RequestBodyAnnotation requestBodyAnnotation;
4166

67+
/**
68+
* Request part annotation.
69+
*/
4270
private RequestPartAnnotation requestPartAnnotation;
4371

72+
/**
73+
* Map of exception advice annotations.
74+
*/
4475
private Map<String, ExceptionAdviceAnnotation> exceptionAdviceAnnotations;
4576

4677
/**
47-
* `javax.websocket.server.ServerEndpoint` info
78+
* WebSocket server endpoint annotation.
79+
* <p>
80+
* javax.websocket.server.ServerEndpoint
81+
* jakarta.websocket.server.ServerEndpoint
82+
* </p>
4883
*/
4984
private ServerEndpointAnnotation serverEndpointAnnotation;
5085

src/main/java/com/ly/doc/template/IRestDocTemplate.java

Lines changed: 121 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -303,39 +303,116 @@ default List<ApiDoc> handleTagsApiDoc(List<ApiDoc> apiDocList) {
303303
return apiDocs;
304304
}
305305

306+
/**
307+
* Retrieves the annotations of the specified class, including those inherited from its superclasses
308+
* and interfaces, but only considering mapping annotations for inheritance.
309+
*
310+
* <p>This method first checks if the specified class has both entry and mapping annotations. If it does,
311+
* it returns the annotations of the class directly. If not, it recursively retrieves the mapping annotations
312+
* from its superclasses and interfaces.</p>
313+
*
314+
* @param cls the class whose annotations are to be retrieved
315+
* @param frameworkAnnotations the framework annotations to be used for filtering
316+
* @return a list of annotations for the specified class, including inherited mapping annotations
317+
*/
306318
default List<JavaAnnotation> getClassAnnotations(JavaClass cls, FrameworkAnnotations frameworkAnnotations) {
319+
// Retrieve the annotations of the specified class
307320
List<JavaAnnotation> annotationsList = new ArrayList<>(cls.getAnnotations());
308-
boolean flag = annotationsList.stream().anyMatch(item -> {
321+
322+
// Get entry annotations from framework annotations
323+
Map<String, EntryAnnotation> entryAnnotationMap = Objects.isNull(frameworkAnnotations.getEntryAnnotations())
324+
? Collections.emptyMap()
325+
: frameworkAnnotations.getEntryAnnotations();
326+
327+
// Get mapping annotations from framework annotations
328+
Map<String, MappingAnnotation> mappingAnnotationMap = Objects.isNull(frameworkAnnotations.getMappingAnnotations())
329+
? Collections.emptyMap()
330+
: frameworkAnnotations.getMappingAnnotations();
331+
332+
// Check if the class has both entry and mapping annotations
333+
boolean hasEntryAndMappingAnnotation = annotationsList.stream().anyMatch(item -> {
309334
String annotationName = item.getType().getValue();
310335
String fullyName = item.getType().getFullyQualifiedName();
311-
Map<String, EntryAnnotation> entryAnnotationMap = frameworkAnnotations.getEntryAnnotations();
312-
if (Objects.isNull(entryAnnotationMap)) {
313-
entryAnnotationMap = Collections.emptyMap();
314-
}
315-
Map<String, MappingAnnotation> mappingAnnotationMap = frameworkAnnotations.getMappingAnnotations();
316-
if (Objects.isNull(mappingAnnotationMap)) {
317-
mappingAnnotationMap = Collections.emptyMap();
318-
}
319336
return (entryAnnotationMap.containsKey(annotationName) || entryAnnotationMap.containsKey(fullyName)) &&
320337
(mappingAnnotationMap.containsKey(annotationName) || mappingAnnotationMap.containsKey(fullyName));
321338
});
322-
// child override parent set
323-
if (flag) {
339+
340+
// If the class has both entry and mapping annotations, return its annotations directly
341+
if (hasEntryAndMappingAnnotation) {
342+
return annotationsList;
343+
}
344+
345+
// Inherit mapping annotations from superclass, if any
346+
JavaClass superJavaClass = cls.getSuperJavaClass();
347+
if (Objects.nonNull(superJavaClass) && !"Object".equals(superJavaClass.getSimpleName())) {
348+
List<JavaAnnotation> superAnnotations = this.getClassAnnotations(superJavaClass, frameworkAnnotations);
349+
annotationsList.addAll(superAnnotations);
350+
}
351+
352+
// Inherit mapping annotations from interfaces, if any
353+
List<JavaClass> interfaceList = cls.getInterfaces();
354+
if (CollectionUtil.isNotEmpty(interfaceList)) {
355+
for (JavaClass javaInterface : interfaceList) {
356+
List<JavaAnnotation> interfaceAnnotations = this.getClassAnnotations(javaInterface, frameworkAnnotations);
357+
annotationsList.addAll(interfaceAnnotations);
358+
}
359+
}
360+
return annotationsList;
361+
}
362+
363+
364+
/**
365+
* Retrieves the annotations of the specified class, including those inherited from its superclasses
366+
* and interfaces, but only considering mapping annotations for inheritance.
367+
*
368+
* <p>This method retrieves the annotations of the specified class and recursively collects
369+
* mapping annotations from its superclasses and interfaces.</p>
370+
*
371+
* @param cls the class whose annotations are to be retrieved
372+
* @param mappingAnnotationMap the map of mapping annotations used to filter and inherit annotations
373+
* @return a list of annotations for the specified class, including inherited mapping annotations
374+
*/
375+
default List<JavaAnnotation> getClassAnnotations(JavaClass cls, Map<String, MappingAnnotation> mappingAnnotationMap) {
376+
// Retrieve the annotations of the specified class
377+
List<JavaAnnotation> annotationsList = new ArrayList<>(cls.getAnnotations());
378+
379+
380+
// Check if the class has both mapping annotations
381+
boolean hasMappingAnnotation = annotationsList.stream().anyMatch(item -> {
382+
String annotationName = item.getType().getValue();
383+
String fullyName = item.getType().getFullyQualifiedName();
384+
return (mappingAnnotationMap.containsKey(annotationName) || mappingAnnotationMap.containsKey(fullyName));
385+
});
386+
387+
// If the class has both mapping annotations, return its annotations directly
388+
if (hasMappingAnnotation) {
324389
return annotationsList;
325390
}
391+
392+
// Inherit mapping annotations from superclass, if any
326393
JavaClass superJavaClass = cls.getSuperJavaClass();
327394
if (Objects.nonNull(superJavaClass) && !"Object".equals(superJavaClass.getSimpleName())) {
328-
annotationsList.addAll(getClassAnnotations(superJavaClass, frameworkAnnotations));
395+
annotationsList.addAll(this.getClassAnnotations(superJavaClass, mappingAnnotationMap).stream()
396+
.filter(annotation -> mappingAnnotationMap.containsKey(annotation.getType().getValue()) ||
397+
mappingAnnotationMap.containsKey(annotation.getType().getFullyQualifiedName()))
398+
.collect(Collectors.toList()));
329399
}
330-
List<JavaClass> interfaseList = cls.getInterfaces();
331-
if (CollectionUtil.isNotEmpty(interfaseList)) {
332-
for (JavaClass javaInterface : interfaseList) {
333-
annotationsList.addAll(getClassAnnotations(javaInterface, frameworkAnnotations));
400+
401+
// Inherit mapping annotations from interfaces, if any
402+
List<JavaClass> interfaceList = cls.getInterfaces();
403+
if (CollectionUtil.isNotEmpty(interfaceList)) {
404+
for (JavaClass javaInterface : interfaceList) {
405+
annotationsList.addAll(this.getClassAnnotations(javaInterface, mappingAnnotationMap).stream()
406+
.filter(annotation -> mappingAnnotationMap.containsKey(annotation.getType().getValue()) ||
407+
mappingAnnotationMap.containsKey(annotation.getType().getFullyQualifiedName()))
408+
.collect(Collectors.toList()));
334409
}
335410
}
411+
336412
return annotationsList;
337413
}
338414

415+
339416
default List<ApiExceptionStatus> buildExceptionStatus(ProjectDocConfigBuilder projectBuilder,
340417
Collection<JavaClass> javaClasses,
341418
FrameworkAnnotations frameworkAnnotations) {
@@ -418,9 +495,14 @@ default List<ApiMethodDoc> buildEntryPointMethod(
418495
boolean paramsDataToTree = projectBuilder.getApiConfig().isParamsDataToTree();
419496
ClassLoader classLoader = projectBuilder.getApiConfig().getClassLoader();
420497
String group = JavaClassUtil.getClassTagsValue(cls, DocTags.GROUP, Boolean.TRUE);
421-
List<JavaAnnotation> classAnnotations = this.getClassAnnotations(cls, frameworkAnnotations);
498+
// Get mapping annotations
499+
Map<String, MappingAnnotation> mappingAnnotations = Objects.isNull(frameworkAnnotations.getMappingAnnotations())
500+
? Collections.emptyMap() : frameworkAnnotations.getMappingAnnotations();
501+
// Get class mappingAnnotations from class and its parent class or interface
502+
List<JavaAnnotation> classAnnotations = this.getClassAnnotations(cls, mappingAnnotations);
503+
422504
String baseUrl = "";
423-
// the requestMapping annotation's consumes value on class
505+
// The requestMapping annotation's consumes value on class
424506
String classMediaType = null;
425507
Map<String, MappingAnnotation> mappingAnnotationMap = frameworkAnnotations.getMappingAnnotations();
426508
for (JavaAnnotation annotation : classAnnotations) {
@@ -1153,16 +1235,36 @@ default ApiRequestExample buildReqJson(DocJavaMethod javaMethod, ApiMethodDoc ap
11531235
return requestExample;
11541236
}
11551237

1238+
/**
1239+
* Determines if the given Java class is a default entry point based on its annotations and the provided framework annotations.
1240+
*
1241+
* @param cls the Java class to check
1242+
* @param frameworkAnnotations the framework annotations to use for the check
1243+
* @return {@code true} if the class is a default entry point, {@code false} otherwise
1244+
*/
11561245
default boolean defaultEntryPoint(JavaClass cls, FrameworkAnnotations frameworkAnnotations) {
1246+
// Check if the class is an annotation or an enum, return false if it is
11571247
if (cls.isAnnotation() || cls.isEnum()) {
11581248
return false;
11591249
}
1250+
1251+
// Check if frameworkAnnotations is null, return false if it is
11601252
if (Objects.isNull(frameworkAnnotations)) {
11611253
return false;
11621254
}
1163-
List<JavaAnnotation> classAnnotations = DocClassUtil.getAnnotations(cls);
1255+
1256+
// Get framework entry annotations
11641257
Map<String, EntryAnnotation> entryAnnotationMap = frameworkAnnotations.getEntryAnnotations();
11651258

1259+
// Check if entry annotations are null, return false if they are
1260+
if (Objects.isNull(frameworkAnnotations.getEntryAnnotations())) {
1261+
return false;
1262+
}
1263+
1264+
// Get class annotations; Note: Spring Entry Annotation is not supported for superclass inheritance
1265+
List<JavaAnnotation> classAnnotations = cls.getAnnotations();
1266+
1267+
// Check if any of the class annotations match the entry annotations
11661268
return classAnnotations.stream().anyMatch(annotation -> {
11671269
String name = annotation.getType().getValue();
11681270
return entryAnnotationMap.containsKey(name);

0 commit comments

Comments
 (0)