Skip to content

Commit 8184f80

Browse files
committed
Performance tunning
1 parent 1820e97 commit 8184f80

File tree

11 files changed

+142
-67
lines changed

11 files changed

+142
-67
lines changed

springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
import org.springdoc.core.service.OpenAPIService;
110110
import org.springdoc.core.service.OperationService;
111111
import org.springdoc.core.utils.PropertyResolverUtils;
112+
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
112113
import org.springdoc.core.utils.SpringDocUtils;
113114

114115
import org.springframework.aop.support.AopUtils;
@@ -131,6 +132,7 @@
131132
import static org.springdoc.core.utils.Constants.DOT;
132133
import static org.springdoc.core.utils.Constants.OPERATION_ATTRIBUTE;
133134
import static org.springdoc.core.utils.Constants.SPRING_MVC_SERVLET_PATH;
135+
import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
134136
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
135137

136138
/**
@@ -392,14 +394,7 @@ protected OpenAPI getOpenApi(String serverBaseUrl, Locale locale) {
392394

393395
// run the optional customizers
394396
List<Server> servers = openAPI.getServers();
395-
List<Server> serversCopy = null;
396-
try {
397-
serversCopy = springDocProviders.jsonMapper()
398-
.readValue(springDocProviders.jsonMapper().writeValueAsString(servers), new TypeReference<List<Server>>() {});
399-
}
400-
catch (JsonProcessingException e) {
401-
LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
402-
}
397+
List<Server> serversCopy = cloneViaJson(servers, new TypeReference<List<Server>>() {}, springDocProviders.jsonMapper());
403398

404399
openAPIService.getContext().getBeansOfType(OpenApiLocaleCustomizer.class).values().forEach(openApiLocaleCustomizer -> openApiLocaleCustomizer.customise(openAPI, finalLocale));
405400
springDocCustomizers.getOpenApiCustomizers().ifPresent(apiCustomizers -> apiCustomizers.forEach(openApiCustomizer -> openApiCustomizer.customise(openAPI)));
@@ -419,10 +414,7 @@ protected OpenAPI getOpenApi(String serverBaseUrl, Locale locale) {
419414
return openAPI;
420415
}
421416
finally {
422-
JavadocProvider javadocProvider = operationParser.getJavadocProvider();
423-
if (javadocProvider != null) {
424-
javadocProvider.clean();
425-
}
417+
SpringDocAnnotationsUtils.clearCache(operationParser.getJavadocProvider());
426418
this.reentrantLock.unlock();
427419
}
428420
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import java.util.Iterator;
3131
import java.util.Map;
3232

33-
import com.fasterxml.jackson.core.JsonProcessingException;
3433
import com.fasterxml.jackson.core.type.TypeReference;
3534
import com.fasterxml.jackson.databind.JavaType;
3635
import io.swagger.v3.core.converter.AnnotatedType;
@@ -41,6 +40,7 @@
4140
import org.slf4j.LoggerFactory;
4241
import org.springdoc.core.providers.ObjectMapperProvider;
4342

43+
import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
4444
import static org.springdoc.core.utils.SpringDocUtils.handleSchemaTypes;
4545

4646
/**
@@ -164,17 +164,12 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
164164
JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
165165
if (javaType != null) {
166166
Class<?> cls = javaType.getRawClass();
167-
if (modelToSchemaMap.containsKey(cls))
168-
try {
169-
Schema schema = modelToSchemaMap.get(cls);
170-
if (springDocObjectMapper.isOpenapi31())
171-
handleSchemaTypes(schema);
172-
return springDocObjectMapper.jsonMapper()
173-
.readValue(springDocObjectMapper.jsonMapper().writeValueAsString(schema), new TypeReference<Schema>() {});
174-
}
175-
catch (JsonProcessingException e) {
176-
LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
177-
}
167+
if (modelToSchemaMap.containsKey(cls)) {
168+
Schema schema = modelToSchemaMap.get(cls);
169+
if (springDocObjectMapper.isOpenapi31())
170+
handleSchemaTypes(schema);
171+
return cloneViaJson(schema, new TypeReference<Schema>() {}, springDocObjectMapper.jsonMapper());
172+
}
178173
if (modelToClassMap.containsKey(cls))
179174
type = new AnnotatedType(modelToClassMap.get(cls)).resolveAsRef(true);
180175
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/JavadocProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,5 @@ public interface JavadocProvider {
106106
/**
107107
* Clean the temp resources.
108108
*/
109-
default void clean() {}
109+
default void clearCache() {}
110110
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/SpringDocJavadocProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ private FieldJavadoc getJavadoc(Field field) {
212212
}
213213

214214
@Override
215-
public void clean() {
215+
public void clearCache() {
216216
classJavadocCache.clear();
217217
}
218218
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import org.springdoc.core.models.RequestBodyInfo;
7878
import org.springdoc.core.properties.SpringDocConfigProperties.ApiDocs.OpenApiVersion;
7979
import org.springdoc.core.providers.JavadocProvider;
80+
import org.springdoc.core.providers.ObjectMapperProvider;
8081
import org.springdoc.core.utils.SchemaUtils;
8182
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
8283

@@ -102,6 +103,7 @@
102103

103104
import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation;
104105
import static org.springdoc.core.service.GenericParameterService.isFile;
106+
import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
105107
import static org.springdoc.core.utils.SpringDocUtils.getParameterAnnotations;
106108
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
107109
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
@@ -674,6 +676,8 @@ public void applyBeanValidatorAnnotations(final MethodParameter methodParameter,
674676
java.lang.reflect.AnnotatedType[] typeArgs = paramType.getAnnotatedActualTypeArguments();
675677
for (java.lang.reflect.AnnotatedType typeArg : typeArgs) {
676678
List<Annotation> genericAnnotations = Arrays.stream(typeArg.getAnnotations()).toList();
679+
Schema schemaItemsClone = cloneViaJson(schema.getItems(), Schema.class, ObjectMapperProvider.createJson(parameterBuilder.getPropertyResolverUtils().getSpringDocConfigProperties()));
680+
schema.items(schemaItemsClone);
677681
SchemaUtils.applyValidationsToSchema(schema.getItems(), genericAnnotations, openapiVersion);
678682
}
679683
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@
5151
import java.util.stream.Stream;
5252

5353
import com.fasterxml.jackson.annotation.JsonView;
54-
import com.fasterxml.jackson.core.JsonProcessingException;
55-
import com.fasterxml.jackson.databind.ObjectMapper;
5654
import io.swagger.v3.core.util.AnnotationsUtils;
5755
import io.swagger.v3.oas.models.Components;
5856
import io.swagger.v3.oas.models.Operation;
@@ -99,6 +97,7 @@
9997
import static org.springdoc.core.utils.SpringDocAnnotationsUtils.extractSchema;
10098
import static org.springdoc.core.utils.SpringDocAnnotationsUtils.getContent;
10199
import static org.springdoc.core.utils.SpringDocAnnotationsUtils.mergeSchema;
100+
import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
102101
import static org.springdoc.core.utils.SpringDocUtils.getParameterAnnotations;
103102

104103
/**
@@ -753,17 +752,7 @@ private Map<String, ApiResponse> getGenericMapResponse(HandlerMethod handlerMeth
753752
}
754753
}
755754
}
756-
757-
LinkedHashMap<String, ApiResponse> genericApiResponsesClone;
758-
try {
759-
ObjectMapper objectMapper = ObjectMapperProvider.createJson(springDocConfigProperties);
760-
genericApiResponsesClone = objectMapper.readValue(objectMapper.writeValueAsString(genericApiResponseMap), ApiResponses.class);
761-
return genericApiResponsesClone;
762-
}
763-
catch (JsonProcessingException e) {
764-
LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
765-
return genericApiResponseMap;
766-
}
755+
return cloneViaJson(genericApiResponseMap, ApiResponses.class, ObjectMapperProvider.createJson(springDocConfigProperties));
767756
}
768757
finally {
769758
reentrantLock.unlock();

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import java.util.stream.Collectors;
4848
import java.util.stream.Stream;
4949

50-
import com.fasterxml.jackson.core.JsonProcessingException;
5150
import com.fasterxml.jackson.databind.ObjectMapper;
5251
import io.swagger.v3.core.jackson.TypeNameResolver;
5352
import io.swagger.v3.core.util.AnnotationsUtils;
@@ -98,6 +97,7 @@
9897
import static org.springdoc.core.utils.Constants.DEFAULT_SERVER_DESCRIPTION;
9998
import static org.springdoc.core.utils.Constants.DEFAULT_TITLE;
10099
import static org.springdoc.core.utils.Constants.DEFAULT_VERSION;
100+
import static org.springdoc.core.utils.SpringDocUtils.cloneViaJson;
101101
import static org.springdoc.core.utils.SpringDocUtils.getConfig;
102102

103103
/**
@@ -246,14 +246,7 @@ public OpenAPI build(Locale locale) {
246246
calculatedOpenAPI.setPaths(new Paths());
247247
}
248248
else {
249-
try {
250-
ObjectMapper objectMapper = new ObjectMapper();
251-
calculatedOpenAPI = objectMapper.readValue(objectMapper.writeValueAsString(openAPI), OpenAPI.class);
252-
}
253-
catch (JsonProcessingException e) {
254-
LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
255-
calculatedOpenAPI = openAPI;
256-
}
249+
calculatedOpenAPI = cloneViaJson(openAPI, OpenAPI.class, new ObjectMapper());
257250
}
258251

259252
if (apiDef.isPresent()) {

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.ArrayList;
3333
import java.util.Arrays;
3434
import java.util.Collections;
35+
import java.util.HashMap;
3536
import java.util.LinkedHashMap;
3637
import java.util.LinkedHashSet;
3738
import java.util.List;
@@ -44,6 +45,8 @@
4445
import com.fasterxml.jackson.annotation.JsonView;
4546
import com.fasterxml.jackson.databind.ObjectMapper;
4647
import io.swagger.v3.core.converter.AnnotatedType;
48+
import io.swagger.v3.core.converter.ModelConverterContext;
49+
import io.swagger.v3.core.converter.ModelConverterContextImpl;
4750
import io.swagger.v3.core.converter.ModelConverters;
4851
import io.swagger.v3.core.converter.ResolvedSchema;
4952
import io.swagger.v3.core.util.AnnotationsUtils;
@@ -65,6 +68,7 @@
6568
import org.apache.commons.lang3.StringUtils;
6669
import org.slf4j.Logger;
6770
import org.slf4j.LoggerFactory;
71+
import org.springdoc.core.providers.JavadocProvider;
6872

6973
import org.springframework.core.MethodParameter;
7074
import org.springframework.core.annotation.AnnotationUtils;
@@ -91,6 +95,11 @@ public class SpringDocAnnotationsUtils extends AnnotationsUtils {
9195
*/
9296
private static final List<Class> ANNOTATIONS_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());
9397

98+
/**
99+
* The reusable context
100+
*/
101+
private static final ThreadLocal<Map<Boolean, ModelConverterContext>> MODEL_CONVERTER_CONTEXT_MAP = ThreadLocal.withInitial(HashMap::new);
102+
94103
static {
95104
ANNOTATIONS_TO_IGNORE.add(Hidden.class);
96105
ANNOTATIONS_TO_IGNORE.add(JsonIgnore.class);
@@ -131,18 +140,16 @@ public static Schema resolveSchemaFromType(Class<?> schemaImplementation, Compon
131140
public static Schema extractSchema(Components components, Type returnType, JsonView jsonView, Annotation[] annotations, SpecVersion specVersion) {
132141
if (returnType == null) return null;
133142
Schema schemaN = null;
134-
ResolvedSchema resolvedSchema;
135143
boolean openapi31 = SpecVersion.V31 == specVersion;
136-
try {
137-
resolvedSchema = ModelConverters.getInstance(openapi31)
138-
.resolveAsResolvedSchema(
139-
new AnnotatedType(returnType)
140-
.resolveAsRef(true).jsonViewAnnotation(jsonView).ctxAnnotations(annotations));
141-
}
142-
catch (Exception e) {
143-
LOGGER.warn(Constants.GRACEFUL_EXCEPTION_OCCURRED, e);
144-
return null;
145-
}
144+
if (jsonView != null)
145+
annotations = ArrayUtils.addAll(annotations, jsonView);
146+
147+
AnnotatedType annotatedType = new AnnotatedType(returnType)
148+
.resolveAsRef(true)
149+
.jsonViewAnnotation(jsonView)
150+
.ctxAnnotations(annotations);
151+
ResolvedSchema resolvedSchema = resolveAsResolvedSchema(openapi31, annotatedType);
152+
146153
if (resolvedSchema != null) {
147154
Map<String, Schema> schemaMap = resolvedSchema.referencedSchemas;
148155
if (!CollectionUtils.isEmpty(schemaMap) && components != null) {
@@ -496,4 +503,49 @@ public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotat
496503
}
497504
return headerMap;
498505
}
506+
507+
/**
508+
* Clear cache.
509+
*
510+
* @param javadocProvider the javadoc provider
511+
*/
512+
public static void clearCache(JavadocProvider javadocProvider) {
513+
if (javadocProvider != null)
514+
javadocProvider.clearCache();
515+
MODEL_CONVERTER_CONTEXT_MAP.remove();;
516+
}
517+
518+
/**
519+
* Resolve as resolved schema resolved schema.
520+
*
521+
* @param openapi31 the openapi 31
522+
* @param type the type
523+
* @return the resolved schema
524+
*/
525+
private static ResolvedSchema resolveAsResolvedSchema(boolean openapi31, AnnotatedType type) {
526+
try {
527+
ModelConverterContext modelConverterContext = getModelConverterContext(openapi31);
528+
ResolvedSchema resolvedSchema = new ResolvedSchema();
529+
resolvedSchema.schema = modelConverterContext.resolve(type);
530+
resolvedSchema.referencedSchemas = modelConverterContext.getDefinedModels();
531+
return resolvedSchema;
532+
}
533+
catch (Exception e) {
534+
LOGGER.warn(Constants.GRACEFUL_EXCEPTION_OCCURRED, e);
535+
return null;
536+
}
537+
}
538+
539+
/**
540+
* Gets model converter context.
541+
*
542+
* @param openapi31 the openapi 31
543+
* @return the model converter context
544+
*/
545+
private static ModelConverterContext getModelConverterContext(boolean openapi31) {
546+
Map<Boolean, ModelConverterContext> perThread = MODEL_CONVERTER_CONTEXT_MAP.get();
547+
return perThread.computeIfAbsent(openapi31, key ->
548+
new ModelConverterContextImpl(ModelConverters.getInstance(openapi31).getConverters()));
549+
}
550+
499551
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@
2626

2727
package org.springdoc.core.utils;
2828

29+
import java.io.IOException;
2930
import java.lang.annotation.Annotation;
3031
import java.util.ArrayList;
3132
import java.util.List;
3233
import java.util.function.Predicate;
3334

35+
import com.fasterxml.jackson.core.type.TypeReference;
36+
import com.fasterxml.jackson.databind.ObjectMapper;
3437
import io.swagger.v3.core.converter.AnnotatedType;
3538
import io.swagger.v3.core.util.PrimitiveType;
3639
import io.swagger.v3.oas.models.media.ComposedSchema;
@@ -39,6 +42,8 @@
3942
import jakarta.validation.constraints.NotNull;
4043
import org.apache.commons.lang3.ArrayUtils;
4144
import org.apache.commons.lang3.StringUtils;
45+
import org.slf4j.Logger;
46+
import org.slf4j.LoggerFactory;
4247
import org.springdoc.api.AbstractOpenApiResource;
4348
import org.springdoc.core.converters.AdditionalModelsConverter;
4449
import org.springdoc.core.converters.ConverterUtils;
@@ -66,6 +71,11 @@ public class SpringDocUtils {
6671
*/
6772
private static final SpringDocUtils springDocConfig = new SpringDocUtils();
6873

74+
/**
75+
* The constant LOGGER.
76+
*/
77+
private static final Logger LOGGER = LoggerFactory.getLogger(SpringDocUtils.class);
78+
6979
/**
7080
* Instantiates a new Spring doc utils.
7181
*/
@@ -562,6 +572,48 @@ public SpringDocUtils resetExtraSchemas() {
562572
return this;
563573
}
564574

575+
576+
/**
577+
* Clone via json t.
578+
*
579+
* @param <T> the type parameter
580+
* @param source the source
581+
* @param targetType the target type
582+
* @return the t
583+
*/
584+
public static <T> T cloneViaJson(Object source, Class<T> targetType, ObjectMapper mapper) {
585+
if (source == null) return null;
586+
try {
587+
return mapper.readValue(mapper.writeValueAsBytes(source), targetType);
588+
}
589+
catch (IOException e) {
590+
LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
591+
@SuppressWarnings("unchecked")
592+
T fallback = (T) source;
593+
return fallback;
594+
}
595+
}
596+
597+
/**
598+
* Clone via json t.
599+
*
600+
* @param <T> the type parameter
601+
* @param source the source
602+
* @param typeRef the type ref
603+
* @return the t
604+
*/
605+
public static <T> T cloneViaJson(Object source, TypeReference<T> typeRef, ObjectMapper mapper) {
606+
if (source == null) return null;
607+
try {
608+
return mapper.readValue(mapper.writeValueAsBytes(source), typeRef);
609+
}
610+
catch (IOException e) {
611+
LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage());
612+
@SuppressWarnings("unchecked")
613+
T fallback = (T) source;
614+
return fallback;
615+
}
616+
}
565617
}
566618

567619

0 commit comments

Comments
 (0)