20
20
import java .lang .reflect .Parameter ;
21
21
import java .lang .reflect .Type ;
22
22
import java .util .ArrayList ;
23
+ import java .util .Arrays ;
23
24
import java .util .List ;
25
+ import java .util .Map ;
26
+ import java .util .concurrent .ConcurrentHashMap ;
24
27
import java .util .stream .Stream ;
25
28
26
29
import com .fasterxml .jackson .annotation .JsonProperty ;
67
70
* If none of these annotations are present, the default behavior is to consider the
68
71
* property as required and not to include a description.
69
72
* <p>
73
+ * This class provides caching for method input schema generation to improve performance
74
+ * when the same method signatures are processed multiple times.
70
75
*
71
76
* @author Thomas Vitale
77
+ * @author Seol-JY
72
78
* @since 1.0.0
73
79
*/
74
80
public final class JsonSchemaGenerator {
@@ -81,6 +87,8 @@ public final class JsonSchemaGenerator {
81
87
*/
82
88
private static final boolean PROPERTY_REQUIRED_BY_DEFAULT = true ;
83
89
90
+ private static final Map <String , String > methodSchemaCache = new ConcurrentHashMap <>(256 );
91
+
84
92
private static final SchemaGenerator TYPE_SCHEMA_GENERATOR ;
85
93
86
94
private static final SchemaGenerator SUBTYPE_SCHEMA_GENERATOR ;
@@ -116,8 +124,82 @@ private JsonSchemaGenerator() {
116
124
117
125
/**
118
126
* Generate a JSON Schema for a method's input parameters.
127
+ *
128
+ * <p>
129
+ * This method uses caching to improve performance when the same method signature is
130
+ * processed multiple times. The cache key includes method signature and schema
131
+ * options to ensure correct cache hits.
132
+ * @param method the method to generate schema for
133
+ * @param schemaOptions options for schema generation
134
+ * @return JSON Schema as a string
135
+ * @throws IllegalArgumentException if method is null
119
136
*/
120
137
public static String generateForMethodInput (Method method , SchemaOption ... schemaOptions ) {
138
+ Assert .notNull (method , "method cannot be null" );
139
+
140
+ String cacheKey = buildMethodCacheKey (method , schemaOptions );
141
+ return methodSchemaCache .computeIfAbsent (cacheKey , key -> generateMethodSchemaInternal (method , schemaOptions ));
142
+ }
143
+
144
+ /**
145
+ * Generate a JSON Schema for a class type.
146
+ */
147
+ public static String generateForType (Type type , SchemaOption ... schemaOptions ) {
148
+ Assert .notNull (type , "type cannot be null" );
149
+ ObjectNode schema = TYPE_SCHEMA_GENERATOR .generateSchema (type );
150
+ if ((type == Void .class ) && !schema .has ("properties" )) {
151
+ schema .putObject ("properties" );
152
+ }
153
+ processSchemaOptions (schemaOptions , schema );
154
+ return schema .toPrettyString ();
155
+ }
156
+
157
+ /**
158
+ * Build cache key for method input schema generation.
159
+ *
160
+ * <p>
161
+ * The cache key includes:
162
+ * <ul>
163
+ * <li>Declaring class name</li>
164
+ * <li>Method name</li>
165
+ * <li>Parameter types (including generics)</li>
166
+ * <li>Schema options</li>
167
+ * </ul>
168
+ * @param method the method
169
+ * @param schemaOptions schema generation options
170
+ * @return unique cache key
171
+ */
172
+ private static String buildMethodCacheKey (Method method , SchemaOption ... schemaOptions ) {
173
+ StringBuilder keyBuilder = new StringBuilder (256 );
174
+
175
+ // Class name
176
+ keyBuilder .append (method .getDeclaringClass ().getName ());
177
+ keyBuilder .append ('#' );
178
+
179
+ // Method name
180
+ keyBuilder .append (method .getName ());
181
+ keyBuilder .append ('(' );
182
+
183
+ // Parameter types (including generic information)
184
+ Type [] parameterTypes = method .getGenericParameterTypes ();
185
+ for (int i = 0 ; i < parameterTypes .length ; i ++) {
186
+ if (i > 0 ) {
187
+ keyBuilder .append (',' );
188
+ }
189
+ keyBuilder .append (parameterTypes [i ].getTypeName ());
190
+ }
191
+ keyBuilder .append (')' );
192
+
193
+ // Schema options
194
+ if (schemaOptions .length > 0 ) {
195
+ keyBuilder .append (':' );
196
+ keyBuilder .append (Arrays .toString (schemaOptions ));
197
+ }
198
+
199
+ return keyBuilder .toString ();
200
+ }
201
+
202
+ private static String generateMethodSchemaInternal (Method method , SchemaOption ... schemaOptions ) {
121
203
ObjectNode schema = JsonParser .getObjectMapper ().createObjectNode ();
122
204
schema .put ("$schema" , SchemaVersion .DRAFT_2020_12 .getIdentifier ());
123
205
schema .put ("type" , "object" );
@@ -155,19 +237,6 @@ public static String generateForMethodInput(Method method, SchemaOption... schem
155
237
return schema .toPrettyString ();
156
238
}
157
239
158
- /**
159
- * Generate a JSON Schema for a class type.
160
- */
161
- public static String generateForType (Type type , SchemaOption ... schemaOptions ) {
162
- Assert .notNull (type , "type cannot be null" );
163
- ObjectNode schema = TYPE_SCHEMA_GENERATOR .generateSchema (type );
164
- if ((type == Void .class ) && !schema .has ("properties" )) {
165
- schema .putObject ("properties" );
166
- }
167
- processSchemaOptions (schemaOptions , schema );
168
- return schema .toPrettyString ();
169
- }
170
-
171
240
private static void processSchemaOptions (SchemaOption [] schemaOptions , ObjectNode schema ) {
172
241
if (Stream .of (schemaOptions )
173
242
.noneMatch (option -> option == SchemaOption .ALLOW_ADDITIONAL_PROPERTIES_BY_DEFAULT )) {
0 commit comments